hippofish/packages/frontend/src/components/MkSystemWebhookEditor.vue
おさむのひと 61fae45390
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>
2024-06-08 15:34:19 +09:00

217 lines
5.5 KiB
Vue

<!--
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>