mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-10-31 05:24:13 +00:00 
			
		
		
		
	* feat: 運営のアクティビティが一定期間ない場合は通知+招待制に移行した際に通知 * fix misskey-js.api.md * Revert "feat: 運営のアクティビティが一定期間ない場合は通知+招待制に移行した際に通知" This reverts commit 3ab953bdf87f28411a1a10bce787a23d238cda80. * 通知をやめてユーザ単位でのお知らせ機能に変更 * テスト用実装を戻す * Update packages/backend/src/queue/processors/CheckModeratorsActivityProcessorService.ts Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> * fix remove empty then --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
		
			
				
	
	
		
			312 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			312 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <!--
 | |
| SPDX-FileCopyrightText: syuilo and misskey-project
 | |
| SPDX-License-Identifier: AGPL-3.0-only
 | |
| -->
 | |
| 
 | |
| <template>
 | |
| <MkModalWindow
 | |
| 	ref="dialogEl"
 | |
| 	:width="450"
 | |
| 	:height="590"
 | |
| 	:canClose="true"
 | |
| 	:withOkButton="false"
 | |
| 	:okButtonDisabled="false"
 | |
| 	@click="onCancelClicked"
 | |
| 	@close="onCancelClicked"
 | |
| 	@closed="emit('closed')"
 | |
| >
 | |
| 	<template #header>
 | |
| 		{{ mode === 'create' ? i18n.ts._webhookSettings.createWebhook : i18n.ts._webhookSettings.modifyWebhook }}
 | |
| 	</template>
 | |
| 
 | |
| 	<div style="display: flex; flex-direction: column; min-height: 100%;">
 | |
| 		<MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;">
 | |
| 			<MkLoading v-if="loading !== 0"/>
 | |
| 			<div v-else :class="$style.root" class="_gaps_m">
 | |
| 				<MkInput v-model="title">
 | |
| 					<template #label>{{ i18n.ts._webhookSettings.name }}</template>
 | |
| 				</MkInput>
 | |
| 				<MkInput v-model="url">
 | |
| 					<template #label>URL</template>
 | |
| 				</MkInput>
 | |
| 				<MkInput v-model="secret">
 | |
| 					<template #label>{{ i18n.ts._webhookSettings.secret }}</template>
 | |
| 				</MkInput>
 | |
| 				<MkFolder :defaultOpen="true">
 | |
| 					<template #label>{{ i18n.ts._webhookSettings.trigger }}</template>
 | |
| 
 | |
| 					<div class="_gaps">
 | |
| 						<div class="_gaps_s">
 | |
| 							<div :class="$style.switchBox">
 | |
| 								<MkSwitch v-model="events.abuseReport" :disabled="disabledEvents.abuseReport">
 | |
| 									<template #label>{{ i18n.ts._webhookSettings._systemEvents.abuseReport }}</template>
 | |
| 								</MkSwitch>
 | |
| 								<MkButton v-show="mode === 'edit'" transparent :class="$style.testButton" :disabled="!(isActive && events.abuseReport)" @click="test('abuseReport')"><i class="ti ti-send"></i></MkButton>
 | |
| 							</div>
 | |
| 							<div :class="$style.switchBox">
 | |
| 								<MkSwitch v-model="events.abuseReportResolved" :disabled="disabledEvents.abuseReportResolved">
 | |
| 									<template #label>{{ i18n.ts._webhookSettings._systemEvents.abuseReportResolved }}</template>
 | |
| 								</MkSwitch>
 | |
| 								<MkButton v-show="mode === 'edit'" transparent :class="$style.testButton" :disabled="!(isActive && events.abuseReportResolved)" @click="test('abuseReportResolved')"><i class="ti ti-send"></i></MkButton>
 | |
| 							</div>
 | |
| 							<div :class="$style.switchBox">
 | |
| 								<MkSwitch v-model="events.userCreated" :disabled="disabledEvents.userCreated">
 | |
| 									<template #label>{{ i18n.ts._webhookSettings._systemEvents.userCreated }}</template>
 | |
| 								</MkSwitch>
 | |
| 								<MkButton v-show="mode === 'edit'" transparent :class="$style.testButton" :disabled="!(isActive && events.userCreated)" @click="test('userCreated')"><i class="ti ti-send"></i></MkButton>
 | |
| 							</div>
 | |
| 							<div :class="$style.switchBox">
 | |
| 								<MkSwitch v-model="events.inactiveModeratorsWarning" :disabled="disabledEvents.inactiveModeratorsWarning">
 | |
| 									<template #label>{{ i18n.ts._webhookSettings._systemEvents.inactiveModeratorsWarning }}</template>
 | |
| 								</MkSwitch>
 | |
| 								<MkButton v-show="mode === 'edit'" transparent :class="$style.testButton" :disabled="!(isActive && events.inactiveModeratorsWarning)" @click="test('inactiveModeratorsWarning')"><i class="ti ti-send"></i></MkButton>
 | |
| 							</div>
 | |
| 							<div :class="$style.switchBox">
 | |
| 								<MkSwitch v-model="events.inactiveModeratorsInvitationOnlyChanged" :disabled="disabledEvents.inactiveModeratorsInvitationOnlyChanged">
 | |
| 									<template #label>{{ i18n.ts._webhookSettings._systemEvents.inactiveModeratorsInvitationOnlyChanged }}</template>
 | |
| 								</MkSwitch>
 | |
| 								<MkButton v-show="mode === 'edit'" transparent :class="$style.testButton" :disabled="!(isActive && events.inactiveModeratorsInvitationOnlyChanged)" @click="test('inactiveModeratorsInvitationOnlyChanged')"><i class="ti ti-send"></i></MkButton>
 | |
| 							</div>
 | |
| 						</div>
 | |
| 
 | |
| 						<div v-show="mode === 'edit'" :class="$style.description">
 | |
| 							{{ i18n.ts._webhookSettings.testRemarks }}
 | |
| 						</div>
 | |
| 					</div>
 | |
| 				</MkFolder>
 | |
| 
 | |
| 				<MkSwitch v-model="isActive">
 | |
| 					<template #label>{{ i18n.ts.enable }}</template>
 | |
| 				</MkSwitch>
 | |
| 			</div>
 | |
| 		</MkSpacer>
 | |
| 		<div :class="$style.footer" class="_buttonsCenter">
 | |
| 			<MkButton primary rounded :disabled="disableSubmitButton" @click="onSubmitClicked">
 | |
| 				<i class="ti ti-check"></i>
 | |
| 				{{ i18n.ts.ok }}
 | |
| 			</MkButton>
 | |
| 			<MkButton rounded @click="onCancelClicked"><i class="ti ti-x"></i> {{ i18n.ts.cancel }}</MkButton>
 | |
| 		</div>
 | |
| 	</div>
 | |
| </MkModalWindow>
 | |
| </template>
 | |
| 
 | |
| <script setup lang="ts">
 | |
| import { computed, onMounted, ref, shallowRef, toRefs } from 'vue';
 | |
| import * as Misskey from 'misskey-js';
 | |
| import MkInput from '@/components/MkInput.vue';
 | |
| import MkSwitch from '@/components/MkSwitch.vue';
 | |
| import {
 | |
| 	MkSystemWebhookEditorProps,
 | |
| 	MkSystemWebhookResult,
 | |
| 	SystemWebhookEventType,
 | |
| } from '@/components/MkSystemWebhookEditor.impl.js';
 | |
| import { i18n } from '@/i18n.js';
 | |
| import MkButton from '@/components/MkButton.vue';
 | |
| import { misskeyApi } from '@/scripts/misskey-api.js';
 | |
| import MkModalWindow from '@/components/MkModalWindow.vue';
 | |
| import MkFolder from '@/components/MkFolder.vue';
 | |
| import * as os from '@/os.js';
 | |
| 
 | |
| type EventType = {
 | |
| 	abuseReport: boolean;
 | |
| 	abuseReportResolved: boolean;
 | |
| 	userCreated: boolean;
 | |
| 	inactiveModeratorsWarning: boolean;
 | |
| 	inactiveModeratorsInvitationOnlyChanged: boolean;
 | |
| }
 | |
| 
 | |
| const emit = defineEmits<{
 | |
| 	(ev: 'submitted', result: MkSystemWebhookResult): void;
 | |
| 	(ev: 'canceled'): void;
 | |
| 	(ev: 'closed'): void;
 | |
| }>();
 | |
| 
 | |
| const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
 | |
| 
 | |
| const props = defineProps<MkSystemWebhookEditorProps>();
 | |
| 
 | |
| const { mode, id, requiredEvents } = toRefs(props);
 | |
| 
 | |
| const loading = ref<number>(0);
 | |
| 
 | |
| const title = ref<string>('');
 | |
| const url = ref<string>('');
 | |
| const secret = ref<string>('');
 | |
| const events = ref<EventType>({
 | |
| 	abuseReport: true,
 | |
| 	abuseReportResolved: true,
 | |
| 	userCreated: true,
 | |
| 	inactiveModeratorsWarning: true,
 | |
| 	inactiveModeratorsInvitationOnlyChanged: true,
 | |
| });
 | |
| const isActive = ref<boolean>(true);
 | |
| 
 | |
| const disabledEvents = ref<EventType>({
 | |
| 	abuseReport: false,
 | |
| 	abuseReportResolved: false,
 | |
| 	userCreated: false,
 | |
| 	inactiveModeratorsWarning: false,
 | |
| 	inactiveModeratorsInvitationOnlyChanged: false,
 | |
| });
 | |
| 
 | |
| const disableSubmitButton = computed(() => {
 | |
| 	if (!title.value) {
 | |
| 		return true;
 | |
| 	}
 | |
| 	if (!url.value) {
 | |
| 		return true;
 | |
| 	}
 | |
| 	if (!secret.value) {
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	return false;
 | |
| });
 | |
| 
 | |
| async function onSubmitClicked() {
 | |
| 	await loadingScope(async () => {
 | |
| 		const params = {
 | |
| 			isActive: isActive.value,
 | |
| 			name: title.value,
 | |
| 			url: url.value,
 | |
| 			secret: secret.value,
 | |
| 			on: Object.keys(events.value).filter(ev => events.value[ev as keyof EventType]) as SystemWebhookEventType[],
 | |
| 		};
 | |
| 
 | |
| 		try {
 | |
| 			switch (mode.value) {
 | |
| 				case 'create': {
 | |
| 					const result = await misskeyApi('admin/system-webhook/create', params);
 | |
| 					dialogEl.value?.close();
 | |
| 					emit('submitted', result);
 | |
| 					break;
 | |
| 				}
 | |
| 				case 'edit': {
 | |
| 					// eslint-disable-next-line
 | |
| 					const result = await misskeyApi('admin/system-webhook/update', { id: id.value!, ...params });
 | |
| 					dialogEl.value?.close();
 | |
| 					emit('submitted', result);
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 			// eslint-disable-next-line
 | |
| 		} catch (ex: any) {
 | |
| 			const msg = ex.message ?? i18n.ts.internalServerErrorDescription;
 | |
| 			await os.alert({ type: 'error', title: i18n.ts.error, text: msg });
 | |
| 			dialogEl.value?.close();
 | |
| 			emit('canceled');
 | |
| 		}
 | |
| 	});
 | |
| }
 | |
| 
 | |
| function onCancelClicked() {
 | |
| 	dialogEl.value?.close();
 | |
| 	emit('canceled');
 | |
| }
 | |
| 
 | |
| async function loadingScope<T>(fn: () => Promise<T>): Promise<T> {
 | |
| 	loading.value++;
 | |
| 	try {
 | |
| 		return await fn();
 | |
| 	} finally {
 | |
| 		loading.value--;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| async function test(type: Misskey.entities.SystemWebhook['on'][number]): Promise<void> {
 | |
| 	if (!id.value) {
 | |
| 		return Promise.resolve();
 | |
| 	}
 | |
| 
 | |
| 	await os.apiWithDialog('admin/system-webhook/test', {
 | |
| 		webhookId: id.value,
 | |
| 		type,
 | |
| 		override: {
 | |
| 			secret: secret.value,
 | |
| 			url: url.value,
 | |
| 		},
 | |
| 	});
 | |
| }
 | |
| 
 | |
| onMounted(async () => {
 | |
| 	await loadingScope(async () => {
 | |
| 		switch (mode.value) {
 | |
| 			case 'edit': {
 | |
| 				if (!id.value) {
 | |
| 					throw new Error('id is required');
 | |
| 				}
 | |
| 
 | |
| 				try {
 | |
| 					const res = await misskeyApi('admin/system-webhook/show', { id: id.value });
 | |
| 
 | |
| 					title.value = res.name;
 | |
| 					url.value = res.url;
 | |
| 					secret.value = res.secret;
 | |
| 					isActive.value = res.isActive;
 | |
| 					for (const ev of Object.keys(events.value)) {
 | |
| 						events.value[ev] = res.on.includes(ev as SystemWebhookEventType);
 | |
| 					}
 | |
| 					// eslint-disable-next-line @typescript-eslint/no-explicit-any
 | |
| 				} catch (ex: any) {
 | |
| 					const msg = ex.message ?? i18n.ts.internalServerErrorDescription;
 | |
| 					await os.alert({ type: 'error', title: i18n.ts.error, text: msg });
 | |
| 					dialogEl.value?.close();
 | |
| 					emit('canceled');
 | |
| 				}
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		for (const ev of requiredEvents.value ?? []) {
 | |
| 			disabledEvents.value[ev] = true;
 | |
| 		}
 | |
| 	});
 | |
| });
 | |
| </script>
 | |
| 
 | |
| <style module lang="scss">
 | |
| .root {
 | |
| 	display: flex;
 | |
| 	flex-direction: column;
 | |
| 	justify-content: center;
 | |
| 	align-items: stretch;
 | |
| }
 | |
| 
 | |
| .footer {
 | |
| 	position: sticky;
 | |
| 	z-index: 10000;
 | |
| 	bottom: 0;
 | |
| 	left: 0;
 | |
| 	padding: 12px;
 | |
| 	border-top: solid 0.5px var(--MI_THEME-divider);
 | |
| 	background: var(--MI_THEME-acrylicBg);
 | |
| 	-webkit-backdrop-filter: var(--MI-blur, blur(15px));
 | |
| 	backdrop-filter: var(--MI-blur, blur(15px));
 | |
| }
 | |
| 
 | |
| .switchBox {
 | |
| 	display: flex;
 | |
| 	align-items: center;
 | |
| 	justify-content: start;
 | |
| 
 | |
| 	.testButton {
 | |
| 		$buttonSize: 28px;
 | |
| 		padding: 0;
 | |
| 		width: $buttonSize;
 | |
| 		min-width: $buttonSize;
 | |
| 		max-width: $buttonSize;
 | |
| 		height: $buttonSize;
 | |
| 		margin-left: auto;
 | |
| 		line-height: normal;
 | |
| 		font-size: 90%;
 | |
| 		border-radius: 9999px;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| .description {
 | |
| 	font-size: 0.85em;
 | |
| 	padding: 8px 0 0 0;
 | |
| 	color: var(--MI_THEME-fgTransparentWeak);
 | |
| }
 | |
| </style>
 |