feat: 通報を受けた際にメールまたはWebhookで通知を送出出来るようにする (#13758)
* feat: 通報を受けた際にメールまたはWebhookで通知を送出出来るようにする * モデログに対応&エンドポイントを単一オブジェクトでのサポートに変更(API経由で大量に作るシチュエーションもないと思うので) * fix spdx * fix migration * fix migration * fix models * add e2e webhook * tweak * fix modlog * fix bugs * add tests and fix bugs * add tests and fix bugs * add tests * fix path * regenerate locale * 混入除去 * 混入除去 * add abuseReportResolved * fix pnpm-lock.yaml * add abuseReportResolved test * fix bugs * fix ui * add tests * fix CHANGELOG.md * add tests * add RoleService.getModeratorIds tests * WebhookServiceをUserとSystemに分割 * fix CHANGELOG.md * fix test * insertOneを使う用に * fix * regenerate locales * revert version * separate webhook job queue * fix * 🎨 * Update QueueProcessorService.ts --------- Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com> Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
217
packages/frontend/src/components/MkSystemWebhookEditor.vue
Normal file
217
packages/frontend/src/components/MkSystemWebhookEditor.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkModalWindow
|
||||
:width="450"
|
||||
:height="590"
|
||||
:canClose="true"
|
||||
:withOkButton="false"
|
||||
:okButtonDisabled="false"
|
||||
@click="onCancelClicked"
|
||||
@close="onCancelClicked"
|
||||
@closed="onCancelClicked"
|
||||
>
|
||||
<template #header>
|
||||
{{ mode === 'create' ? i18n.ts._webhookSettings.createWebhook : i18n.ts._webhookSettings.modifyWebhook }}
|
||||
</template>
|
||||
<MkSpacer :marginMin="20" :marginMax="28">
|
||||
<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.events }}</template>
|
||||
|
||||
<div class="_gaps_s">
|
||||
<MkSwitch v-model="events.abuseReport" :disabled="disabledEvents.abuseReport">
|
||||
<template #label>{{ i18n.ts._webhookSettings._systemEvents.abuseReport }}</template>
|
||||
</MkSwitch>
|
||||
<MkSwitch v-model="events.abuseReportResolved" :disabled="disabledEvents.abuseReportResolved">
|
||||
<template #label>{{ i18n.ts._webhookSettings._systemEvents.abuseReportResolved }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkSwitch v-model="isActive">
|
||||
<template #label>{{ i18n.ts.enable }}</template>
|
||||
</MkSwitch>
|
||||
|
||||
<div :class="$style.footer" class="_buttonsCenter">
|
||||
<MkButton primary :disabled="disableSubmitButton" @click="onSubmitClicked">
|
||||
<i class="ti ti-check"></i>
|
||||
{{ i18n.ts.ok }}
|
||||
</MkButton>
|
||||
<MkButton @click="onCancelClicked"><i class="ti ti-x"></i> {{ i18n.ts.cancel }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</MkModalWindow>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, toRefs } from 'vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
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;
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'submitted', result: MkSystemWebhookResult): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
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,
|
||||
});
|
||||
const isActive = ref<boolean>(true);
|
||||
|
||||
const disabledEvents = ref<EventType>({
|
||||
abuseReport: false,
|
||||
abuseReportResolved: 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);
|
||||
emit('submitted', result);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
// eslint-disable-next-line
|
||||
const result = await misskeyApi('admin/system-webhook/update', { id: id.value!, ...params });
|
||||
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 });
|
||||
emit('closed');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onCancelClicked() {
|
||||
emit('closed');
|
||||
}
|
||||
|
||||
async function loadingScope<T>(fn: () => Promise<T>): Promise<T> {
|
||||
loading.value++;
|
||||
try {
|
||||
return await fn();
|
||||
} finally {
|
||||
loading.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
|
||||
} catch (ex: any) {
|
||||
const msg = ex.message ?? i18n.ts.internalServerErrorDescription;
|
||||
await os.alert({ type: 'error', title: i18n.ts.error, text: msg });
|
||||
emit('closed');
|
||||
}
|
||||
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 {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user