feature: ユーザ作成時にSystemWebhookを発信できるようにする (#14321)
* feature: ユーザ作成時にSystemWebhookを発信できるようにする * fix CHANGELOG.md
This commit is contained in:
parent
0f0660d49e
commit
72bc789746
12 changed files with 237 additions and 62 deletions
|
@ -8,6 +8,7 @@
|
||||||
- Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705
|
- Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705
|
||||||
- Feat: ユーザーのアイコン/バナーの変更可否をロールで設定可能に
|
- Feat: ユーザーのアイコン/バナーの変更可否をロールで設定可能に
|
||||||
- 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます
|
- 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます
|
||||||
|
- Feat: ユーザ作成時にSystemWebhookを送信可能に #14281
|
||||||
- Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
|
- Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
|
||||||
- Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
|
- Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
|
||||||
- Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正
|
- Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正
|
||||||
|
|
4
locales/index.d.ts
vendored
4
locales/index.d.ts
vendored
|
@ -9392,6 +9392,10 @@ export interface Locale extends ILocale {
|
||||||
* ユーザーからの通報を処理したとき
|
* ユーザーからの通報を処理したとき
|
||||||
*/
|
*/
|
||||||
"abuseReportResolved": string;
|
"abuseReportResolved": string;
|
||||||
|
/**
|
||||||
|
* ユーザーが作成されたとき
|
||||||
|
*/
|
||||||
|
"userCreated": string;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Webhookを削除しますか?
|
* Webhookを削除しますか?
|
||||||
|
|
|
@ -2491,6 +2491,7 @@ _webhookSettings:
|
||||||
_systemEvents:
|
_systemEvents:
|
||||||
abuseReport: "ユーザーから通報があったとき"
|
abuseReport: "ユーザーから通報があったとき"
|
||||||
abuseReportResolved: "ユーザーからの通報を処理したとき"
|
abuseReportResolved: "ユーザーからの通報を処理したとき"
|
||||||
|
userCreated: "ユーザーが作成されたとき"
|
||||||
deleteConfirm: "Webhookを削除しますか?"
|
deleteConfirm: "Webhookを削除しますか?"
|
||||||
|
|
||||||
_abuseReport:
|
_abuseReport:
|
||||||
|
|
|
@ -44,7 +44,7 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 管理者用Redisイベントを用いて{@link abuseReports}の内容を管理者各位に通知する.
|
* 管理者用Redisイベントを用いて{@link abuseReports}の内容を管理者各位に通知する.
|
||||||
* 通知先ユーザは{@link RoleService.getModeratorIds}の取得結果に依る.
|
* 通知先ユーザは{@link getModeratorIds}の取得結果に依る.
|
||||||
*
|
*
|
||||||
* @see RoleService.getModeratorIds
|
* @see RoleService.getModeratorIds
|
||||||
* @see GlobalEventService.publishAdminStream
|
* @see GlobalEventService.publishAdminStream
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { bindThis } from '@/decorators.js';
|
||||||
import UsersChart from '@/core/chart/charts/users.js';
|
import UsersChart from '@/core/chart/charts/users.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
|
import { UserService } from '@/core/UserService.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SignupService {
|
export class SignupService {
|
||||||
|
@ -35,6 +36,7 @@ export class SignupService {
|
||||||
private usedUsernamesRepository: UsedUsernamesRepository,
|
private usedUsernamesRepository: UsedUsernamesRepository,
|
||||||
|
|
||||||
private utilityService: UtilityService,
|
private utilityService: UtilityService,
|
||||||
|
private userService: UserService,
|
||||||
private userEntityService: UserEntityService,
|
private userEntityService: UserEntityService,
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private metaService: MetaService,
|
private metaService: MetaService,
|
||||||
|
@ -148,7 +150,8 @@ export class SignupService {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.usersChart.update(account, true);
|
this.usersChart.update(account, true).then();
|
||||||
|
this.userService.notifySystemWebhook(account, 'userCreated').then();
|
||||||
|
|
||||||
return { account, secret };
|
return { account, secret };
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,15 +8,18 @@ import type { FollowingsRepository, UsersRepository } from '@/models/_.js';
|
||||||
import type { MiUser } from '@/models/User.js';
|
import type { MiUser } from '@/models/User.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||||
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
private usersRepository: UsersRepository,
|
private usersRepository: UsersRepository,
|
||||||
|
|
||||||
@Inject(DI.followingsRepository)
|
@Inject(DI.followingsRepository)
|
||||||
private followingsRepository: FollowingsRepository,
|
private followingsRepository: FollowingsRepository,
|
||||||
|
private systemWebhookService: SystemWebhookService,
|
||||||
|
private userEntityService: UserEntityService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,4 +53,23 @@ export class UserService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SystemWebhookを用いてユーザに関する操作内容を管理者各位に通知する.
|
||||||
|
* ここではJobQueueへのエンキューのみを行うため、即時実行されない.
|
||||||
|
*
|
||||||
|
* @see SystemWebhookService.enqueueSystemWebhook
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public async notifySystemWebhook(user: MiUser, type: 'userCreated') {
|
||||||
|
const packedUser = await this.userEntityService.pack(user, null, { schema: 'UserLite' });
|
||||||
|
const recipientWebhookIds = await this.systemWebhookService.fetchSystemWebhooks({ isActive: true, on: [type] });
|
||||||
|
for (const webhookId of recipientWebhookIds) {
|
||||||
|
await this.systemWebhookService.enqueueSystemWebhook(
|
||||||
|
webhookId,
|
||||||
|
type,
|
||||||
|
packedUser,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ export const systemWebhookEventTypes = [
|
||||||
'abuseReport',
|
'abuseReport',
|
||||||
// 通報を処理したとき
|
// 通報を処理したとき
|
||||||
'abuseReportResolved',
|
'abuseReportResolved',
|
||||||
|
// ユーザが作成された時
|
||||||
|
'userCreated',
|
||||||
] as const;
|
] as const;
|
||||||
export type SystemWebhookEventType = typeof systemWebhookEventTypes[number];
|
export type SystemWebhookEventType = typeof systemWebhookEventTypes[number];
|
||||||
|
|
||||||
|
|
|
@ -5,65 +5,24 @@
|
||||||
|
|
||||||
import { entities } from 'misskey-js';
|
import { entities } from 'misskey-js';
|
||||||
import { beforeEach, describe, test } from '@jest/globals';
|
import { beforeEach, describe, test } from '@jest/globals';
|
||||||
import Fastify from 'fastify';
|
import {
|
||||||
import { api, randomString, role, signup, startJobQueue, UserToken } from '../../utils.js';
|
api,
|
||||||
|
captureWebhook,
|
||||||
|
randomString,
|
||||||
|
role,
|
||||||
|
signup,
|
||||||
|
startJobQueue,
|
||||||
|
UserToken,
|
||||||
|
WEBHOOK_HOST,
|
||||||
|
} from '../../utils.js';
|
||||||
import type { INestApplicationContext } from '@nestjs/common';
|
import type { INestApplicationContext } from '@nestjs/common';
|
||||||
|
|
||||||
const WEBHOOK_HOST = 'http://localhost:15080';
|
|
||||||
const WEBHOOK_PORT = 15080;
|
|
||||||
process.env.NODE_ENV = 'test';
|
|
||||||
|
|
||||||
describe('[シナリオ] ユーザ通報', () => {
|
describe('[シナリオ] ユーザ通報', () => {
|
||||||
let queue: INestApplicationContext;
|
let queue: INestApplicationContext;
|
||||||
let admin: entities.SignupResponse;
|
let admin: entities.SignupResponse;
|
||||||
let alice: entities.SignupResponse;
|
let alice: entities.SignupResponse;
|
||||||
let bob: entities.SignupResponse;
|
let bob: entities.SignupResponse;
|
||||||
|
|
||||||
type SystemWebhookPayload = {
|
|
||||||
server: string;
|
|
||||||
hookId: string;
|
|
||||||
eventId: string;
|
|
||||||
createdAt: string;
|
|
||||||
type: string;
|
|
||||||
body: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
async function captureWebhook<T = SystemWebhookPayload>(postAction: () => Promise<void>): Promise<T> {
|
|
||||||
const fastify = Fastify();
|
|
||||||
|
|
||||||
let timeoutHandle: NodeJS.Timeout | null = null;
|
|
||||||
const result = await new Promise<string>(async (resolve, reject) => {
|
|
||||||
fastify.all('/', async (req, res) => {
|
|
||||||
timeoutHandle && clearTimeout(timeoutHandle);
|
|
||||||
|
|
||||||
const body = JSON.stringify(req.body);
|
|
||||||
res.status(200).send('ok');
|
|
||||||
await fastify.close();
|
|
||||||
resolve(body);
|
|
||||||
});
|
|
||||||
|
|
||||||
await fastify.listen({ port: WEBHOOK_PORT });
|
|
||||||
|
|
||||||
timeoutHandle = setTimeout(async () => {
|
|
||||||
await fastify.close();
|
|
||||||
reject(new Error('timeout'));
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await postAction();
|
|
||||||
} catch (e) {
|
|
||||||
await fastify.close();
|
|
||||||
reject(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await fastify.close();
|
|
||||||
|
|
||||||
return JSON.parse(result) as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createSystemWebhook(args?: Partial<entities.AdminSystemWebhookCreateRequest>, credential?: UserToken): Promise<entities.AdminSystemWebhookCreateResponse> {
|
async function createSystemWebhook(args?: Partial<entities.AdminSystemWebhookCreateRequest>, credential?: UserToken): Promise<entities.AdminSystemWebhookCreateResponse> {
|
||||||
const res = await api(
|
const res = await api(
|
||||||
'admin/system-webhook/create',
|
'admin/system-webhook/create',
|
||||||
|
|
130
packages/backend/test/e2e/synalio/user-create.ts
Normal file
130
packages/backend/test/e2e/synalio/user-create.ts
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { setTimeout } from 'node:timers/promises';
|
||||||
|
import { entities } from 'misskey-js';
|
||||||
|
import { beforeEach, describe, test } from '@jest/globals';
|
||||||
|
import {
|
||||||
|
api,
|
||||||
|
captureWebhook,
|
||||||
|
randomString,
|
||||||
|
role,
|
||||||
|
signup,
|
||||||
|
startJobQueue,
|
||||||
|
UserToken,
|
||||||
|
WEBHOOK_HOST,
|
||||||
|
} from '../../utils.js';
|
||||||
|
import type { INestApplicationContext } from '@nestjs/common';
|
||||||
|
|
||||||
|
describe('[シナリオ] ユーザ作成', () => {
|
||||||
|
let queue: INestApplicationContext;
|
||||||
|
let admin: entities.SignupResponse;
|
||||||
|
|
||||||
|
async function createSystemWebhook(args?: Partial<entities.AdminSystemWebhookCreateRequest>, credential?: UserToken): Promise<entities.AdminSystemWebhookCreateResponse> {
|
||||||
|
const res = await api(
|
||||||
|
'admin/system-webhook/create',
|
||||||
|
{
|
||||||
|
isActive: true,
|
||||||
|
name: randomString(),
|
||||||
|
on: ['userCreated'],
|
||||||
|
url: WEBHOOK_HOST,
|
||||||
|
secret: randomString(),
|
||||||
|
...args,
|
||||||
|
},
|
||||||
|
credential ?? admin,
|
||||||
|
);
|
||||||
|
return res.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
queue = await startJobQueue();
|
||||||
|
admin = await signup({ username: 'admin' });
|
||||||
|
|
||||||
|
await role(admin, { isAdministrator: true });
|
||||||
|
}, 1000 * 60 * 2);
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await queue.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
describe('SystemWebhook', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
const webhooks = await api('admin/system-webhook/list', {}, admin);
|
||||||
|
for (const webhook of webhooks.body) {
|
||||||
|
await api('admin/system-webhook/delete', { id: webhook.id }, admin);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ユーザが作成された -> userCreatedが送出される', async () => {
|
||||||
|
const webhook = await createSystemWebhook({
|
||||||
|
on: ['userCreated'],
|
||||||
|
isActive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let alice: any = null;
|
||||||
|
const webhookBody = await captureWebhook(async () => {
|
||||||
|
alice = await signup({ username: 'alice' });
|
||||||
|
});
|
||||||
|
|
||||||
|
// webhookの送出後にいろいろやってるのでちょっと待つ必要がある
|
||||||
|
await setTimeout(2000);
|
||||||
|
|
||||||
|
console.log(alice);
|
||||||
|
console.log(JSON.stringify(webhookBody, null, 2));
|
||||||
|
|
||||||
|
expect(webhookBody.hookId).toBe(webhook.id);
|
||||||
|
expect(webhookBody.type).toBe('userCreated');
|
||||||
|
|
||||||
|
const body = webhookBody.body as entities.UserLite;
|
||||||
|
expect(alice.id).toBe(body.id);
|
||||||
|
expect(alice.name).toBe(body.name);
|
||||||
|
expect(alice.username).toBe(body.username);
|
||||||
|
expect(alice.host).toBe(body.host);
|
||||||
|
expect(alice.avatarUrl).toBe(body.avatarUrl);
|
||||||
|
expect(alice.avatarBlurhash).toBe(body.avatarBlurhash);
|
||||||
|
expect(alice.avatarDecorations).toEqual(body.avatarDecorations);
|
||||||
|
expect(alice.isBot).toBe(body.isBot);
|
||||||
|
expect(alice.isCat).toBe(body.isCat);
|
||||||
|
expect(alice.instance).toEqual(body.instance);
|
||||||
|
expect(alice.emojis).toEqual(body.emojis);
|
||||||
|
expect(alice.onlineStatus).toBe(body.onlineStatus);
|
||||||
|
expect(alice.badgeRoles).toEqual(body.badgeRoles);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ユーザ作成 -> userCreatedが未許可の場合は送出されない', async () => {
|
||||||
|
await createSystemWebhook({
|
||||||
|
on: [],
|
||||||
|
isActive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let alice: any = null;
|
||||||
|
const webhookBody = await captureWebhook(async () => {
|
||||||
|
alice = await signup({ username: 'alice' });
|
||||||
|
}).catch(e => e.message);
|
||||||
|
|
||||||
|
expect(webhookBody).toBe('timeout');
|
||||||
|
expect(alice.id).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ユーザ作成 -> Webhookが無効の場合は送出されない', async () => {
|
||||||
|
await createSystemWebhook({
|
||||||
|
on: ['userCreated'],
|
||||||
|
isActive: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let alice: any = null;
|
||||||
|
const webhookBody = await captureWebhook(async () => {
|
||||||
|
alice = await signup({ username: 'alice' });
|
||||||
|
}).catch(e => e.message);
|
||||||
|
|
||||||
|
expect(webhookBody).toBe('timeout');
|
||||||
|
expect(alice.id).not.toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -12,13 +12,14 @@ import WebSocket, { ClientOptions } from 'ws';
|
||||||
import fetch, { File, RequestInit, type Headers } from 'node-fetch';
|
import fetch, { File, RequestInit, type Headers } from 'node-fetch';
|
||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
import { JSDOM } from 'jsdom';
|
import { JSDOM } from 'jsdom';
|
||||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
import { type Response } from 'node-fetch';
|
||||||
import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
|
import Fastify from 'fastify';
|
||||||
import { entities } from '../src/postgres.js';
|
import { entities } from '../src/postgres.js';
|
||||||
import { loadConfig } from '../src/config.js';
|
import { loadConfig } from '../src/config.js';
|
||||||
import type * as misskey from 'misskey-js';
|
import type * as misskey from 'misskey-js';
|
||||||
import { type Response } from 'node-fetch';
|
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||||
import { ApiError } from "@/server/api/error.js";
|
import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
|
|
||||||
export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';
|
export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';
|
||||||
|
|
||||||
|
@ -27,11 +28,23 @@ export interface UserToken {
|
||||||
bearer?: boolean;
|
bearer?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SystemWebhookPayload = {
|
||||||
|
server: string;
|
||||||
|
hookId: string;
|
||||||
|
eventId: string;
|
||||||
|
createdAt: string;
|
||||||
|
type: string;
|
||||||
|
body: any;
|
||||||
|
}
|
||||||
|
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
export const port = config.port;
|
export const port = config.port;
|
||||||
export const origin = config.url;
|
export const origin = config.url;
|
||||||
export const host = new URL(config.url).host;
|
export const host = new URL(config.url).host;
|
||||||
|
|
||||||
|
export const WEBHOOK_HOST = 'http://localhost:15080';
|
||||||
|
export const WEBHOOK_PORT = 15080;
|
||||||
|
|
||||||
export const cookie = (me: UserToken): string => {
|
export const cookie = (me: UserToken): string => {
|
||||||
return `token=${me.token};`;
|
return `token=${me.token};`;
|
||||||
};
|
};
|
||||||
|
@ -645,3 +658,37 @@ export async function sendEnvResetRequest() {
|
||||||
export function castAsError(obj: Record<string, unknown>): { error: ApiError } {
|
export function castAsError(obj: Record<string, unknown>): { error: ApiError } {
|
||||||
return obj as { error: ApiError };
|
return obj as { error: ApiError };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function captureWebhook<T = SystemWebhookPayload>(postAction: () => Promise<void>, port = WEBHOOK_PORT): Promise<T> {
|
||||||
|
const fastify = Fastify();
|
||||||
|
|
||||||
|
let timeoutHandle: NodeJS.Timeout | null = null;
|
||||||
|
const result = await new Promise<string>(async (resolve, reject) => {
|
||||||
|
fastify.all('/', async (req, res) => {
|
||||||
|
timeoutHandle && clearTimeout(timeoutHandle);
|
||||||
|
|
||||||
|
const body = JSON.stringify(req.body);
|
||||||
|
res.status(200).send('ok');
|
||||||
|
await fastify.close();
|
||||||
|
resolve(body);
|
||||||
|
});
|
||||||
|
|
||||||
|
await fastify.listen({ port });
|
||||||
|
|
||||||
|
timeoutHandle = setTimeout(async () => {
|
||||||
|
await fastify.close();
|
||||||
|
reject(new Error('timeout'));
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await postAction();
|
||||||
|
} catch (e) {
|
||||||
|
await fastify.close();
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await fastify.close();
|
||||||
|
|
||||||
|
return JSON.parse(result) as T;
|
||||||
|
}
|
||||||
|
|
|
@ -40,6 +40,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkSwitch v-model="events.abuseReportResolved" :disabled="disabledEvents.abuseReportResolved">
|
<MkSwitch v-model="events.abuseReportResolved" :disabled="disabledEvents.abuseReportResolved">
|
||||||
<template #label>{{ i18n.ts._webhookSettings._systemEvents.abuseReportResolved }}</template>
|
<template #label>{{ i18n.ts._webhookSettings._systemEvents.abuseReportResolved }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
<MkSwitch v-model="events.userCreated" :disabled="disabledEvents.userCreated">
|
||||||
|
<template #label>{{ i18n.ts._webhookSettings._systemEvents.userCreated }}</template>
|
||||||
|
</MkSwitch>
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
|
@ -78,6 +81,7 @@ import * as os from '@/os.js';
|
||||||
type EventType = {
|
type EventType = {
|
||||||
abuseReport: boolean;
|
abuseReport: boolean;
|
||||||
abuseReportResolved: boolean;
|
abuseReportResolved: boolean;
|
||||||
|
userCreated: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -100,12 +104,14 @@ const secret = ref<string>('');
|
||||||
const events = ref<EventType>({
|
const events = ref<EventType>({
|
||||||
abuseReport: true,
|
abuseReport: true,
|
||||||
abuseReportResolved: true,
|
abuseReportResolved: true,
|
||||||
|
userCreated: true,
|
||||||
});
|
});
|
||||||
const isActive = ref<boolean>(true);
|
const isActive = ref<boolean>(true);
|
||||||
|
|
||||||
const disabledEvents = ref<EventType>({
|
const disabledEvents = ref<EventType>({
|
||||||
abuseReport: false,
|
abuseReport: false,
|
||||||
abuseReportResolved: false,
|
abuseReportResolved: false,
|
||||||
|
userCreated: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const disableSubmitButton = computed(() => {
|
const disableSubmitButton = computed(() => {
|
||||||
|
|
|
@ -4970,7 +4970,7 @@ export type components = {
|
||||||
latestSentAt: string | null;
|
latestSentAt: string | null;
|
||||||
latestStatus: number | null;
|
latestStatus: number | null;
|
||||||
name: string;
|
name: string;
|
||||||
on: ('abuseReport' | 'abuseReportResolved')[];
|
on: ('abuseReport' | 'abuseReportResolved' | 'userCreated')[];
|
||||||
url: string;
|
url: string;
|
||||||
secret: string;
|
secret: string;
|
||||||
};
|
};
|
||||||
|
@ -10042,7 +10042,7 @@ export type operations = {
|
||||||
'application/json': {
|
'application/json': {
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
on: ('abuseReport' | 'abuseReportResolved')[];
|
on: ('abuseReport' | 'abuseReportResolved' | 'userCreated')[];
|
||||||
url: string;
|
url: string;
|
||||||
secret: string;
|
secret: string;
|
||||||
};
|
};
|
||||||
|
@ -10152,7 +10152,7 @@ export type operations = {
|
||||||
content: {
|
content: {
|
||||||
'application/json': {
|
'application/json': {
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
on?: ('abuseReport' | 'abuseReportResolved')[];
|
on?: ('abuseReport' | 'abuseReportResolved' | 'userCreated')[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -10265,7 +10265,7 @@ export type operations = {
|
||||||
id: string;
|
id: string;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
on: ('abuseReport' | 'abuseReportResolved')[];
|
on: ('abuseReport' | 'abuseReportResolved' | 'userCreated')[];
|
||||||
url: string;
|
url: string;
|
||||||
secret: string;
|
secret: string;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue