mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-12-23 00:29:22 +09:00
add current.ts
This commit is contained in:
parent
968fa07662
commit
c5dab0f7f7
@ -8,6 +8,8 @@ import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { MiMeta } from '@/models/Meta.js';
|
||||
import Logger from '@/logger.js';
|
||||
import { LoggerService } from './LoggerService.js';
|
||||
|
||||
export const supportedCaptchaProviders = ['none', 'hcaptcha', 'mcaptcha', 'recaptcha', 'turnstile', 'testcaptcha'] as const;
|
||||
export type CaptchaProvider = typeof supportedCaptchaProviders[number];
|
||||
@ -22,12 +24,35 @@ export const captchaErrorCodes = {
|
||||
} as const;
|
||||
export type CaptchaErrorCode = typeof captchaErrorCodes[keyof typeof captchaErrorCodes];
|
||||
|
||||
export type CaptchaSetting = {
|
||||
provider: CaptchaProvider;
|
||||
hcaptcha: {
|
||||
siteKey: string | null;
|
||||
secretKey: string | null;
|
||||
}
|
||||
mcaptcha: {
|
||||
siteKey: string | null;
|
||||
secretKey: string | null;
|
||||
instanceUrl: string | null;
|
||||
}
|
||||
recaptcha: {
|
||||
siteKey: string | null;
|
||||
secretKey: string | null;
|
||||
}
|
||||
turnstile: {
|
||||
siteKey: string | null;
|
||||
secretKey: string | null;
|
||||
}
|
||||
}
|
||||
|
||||
export class CaptchaError extends Error {
|
||||
public readonly code: CaptchaErrorCode;
|
||||
public readonly cause?: unknown;
|
||||
|
||||
constructor(code: CaptchaErrorCode, message: string) {
|
||||
constructor(code: CaptchaErrorCode, message: string, cause?: unknown) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.cause = cause;
|
||||
this.name = 'CaptchaError';
|
||||
}
|
||||
}
|
||||
@ -48,10 +73,14 @@ type CaptchaResponse = {
|
||||
|
||||
@Injectable()
|
||||
export class CaptchaService {
|
||||
private readonly logger: Logger;
|
||||
|
||||
constructor(
|
||||
private httpRequestService: HttpRequestService,
|
||||
private metaService: MetaService,
|
||||
loggerService: LoggerService,
|
||||
) {
|
||||
this.logger = loggerService.getLogger('captcha');
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@ -126,7 +155,7 @@ export class CaptchaService {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}, { throwErrorWhenResponseNotOk: false });
|
||||
|
||||
if (result.status !== 200) {
|
||||
throw new CaptchaError(captchaErrorCodes.requestFailed, 'mcaptcha-failed: mcaptcha didn\'t return 200 OK');
|
||||
@ -168,6 +197,60 @@ export class CaptchaService {
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async get(): Promise<CaptchaSetting> {
|
||||
const meta = await this.metaService.fetch(true);
|
||||
|
||||
let provider: CaptchaProvider;
|
||||
switch (true) {
|
||||
case meta.enableHcaptcha: {
|
||||
provider = 'hcaptcha';
|
||||
break;
|
||||
}
|
||||
case meta.enableMcaptcha: {
|
||||
provider = 'mcaptcha';
|
||||
break;
|
||||
}
|
||||
case meta.enableRecaptcha: {
|
||||
provider = 'recaptcha';
|
||||
break;
|
||||
}
|
||||
case meta.enableTurnstile: {
|
||||
provider = 'turnstile';
|
||||
break;
|
||||
}
|
||||
case meta.enableTestcaptcha: {
|
||||
provider = 'testcaptcha';
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
provider = 'none';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
provider: provider,
|
||||
hcaptcha: {
|
||||
siteKey: meta.hcaptchaSiteKey,
|
||||
secretKey: meta.hcaptchaSecretKey,
|
||||
},
|
||||
mcaptcha: {
|
||||
siteKey: meta.mcaptchaSitekey,
|
||||
secretKey: meta.mcaptchaSecretKey,
|
||||
instanceUrl: meta.mcaptchaInstanceUrl,
|
||||
},
|
||||
recaptcha: {
|
||||
siteKey: meta.recaptchaSiteKey,
|
||||
secretKey: meta.recaptchaSecretKey,
|
||||
},
|
||||
turnstile: {
|
||||
siteKey: meta.turnstileSiteKey,
|
||||
secretKey: meta.turnstileSecretKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* captchaの設定を更新します. その際、フロントエンド側で受け取ったcaptchaからの戻り値を検証し、passした場合のみ設定を更新します.
|
||||
* 実際の検証処理はサービス内で定義されている各captchaプロバイダの検証関数に委譲します.
|
||||
@ -250,6 +333,7 @@ export class CaptchaService {
|
||||
return operation()
|
||||
.then(() => ({ success: true }) as CaptchaSaveSuccess)
|
||||
.catch(err => {
|
||||
this.logger.info(err);
|
||||
const error = err instanceof CaptchaError
|
||||
? err
|
||||
: new CaptchaError(captchaErrorCodes.unknown, `unknown error: ${err}`);
|
||||
|
@ -28,6 +28,7 @@ import * as ep___admin_avatarDecorations_create from './endpoints/admin/avatar-d
|
||||
import * as ep___admin_avatarDecorations_delete from './endpoints/admin/avatar-decorations/delete.js';
|
||||
import * as ep___admin_avatarDecorations_list from './endpoints/admin/avatar-decorations/list.js';
|
||||
import * as ep___admin_avatarDecorations_update from './endpoints/admin/avatar-decorations/update.js';
|
||||
import * as ep___admin_captcha_current from './endpoints/admin/captcha/current.js';
|
||||
import * as ep___admin_captcha_save from './endpoints/admin/captcha/save.js';
|
||||
import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js';
|
||||
import * as ep___admin_unsetUserAvatar from './endpoints/admin/unset-user-avatar.js';
|
||||
@ -417,6 +418,7 @@ const $admin_avatarDecorations_create: Provider = { provide: 'ep:admin/avatar-de
|
||||
const $admin_avatarDecorations_delete: Provider = { provide: 'ep:admin/avatar-decorations/delete', useClass: ep___admin_avatarDecorations_delete.default };
|
||||
const $admin_avatarDecorations_list: Provider = { provide: 'ep:admin/avatar-decorations/list', useClass: ep___admin_avatarDecorations_list.default };
|
||||
const $admin_avatarDecorations_update: Provider = { provide: 'ep:admin/avatar-decorations/update', useClass: ep___admin_avatarDecorations_update.default };
|
||||
const $admin_captcha_current: Provider = { provide: 'ep:admin/captcha/current', useClass: ep___admin_captcha_current.default };
|
||||
const $admin_captcha_save: Provider = { provide: 'ep:admin/captcha/save', useClass: ep___admin_captcha_save.default };
|
||||
const $admin_deleteAllFilesOfAUser: Provider = { provide: 'ep:admin/delete-all-files-of-a-user', useClass: ep___admin_deleteAllFilesOfAUser.default };
|
||||
const $admin_unsetUserAvatar: Provider = { provide: 'ep:admin/unset-user-avatar', useClass: ep___admin_unsetUserAvatar.default };
|
||||
@ -810,6 +812,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
||||
$admin_avatarDecorations_delete,
|
||||
$admin_avatarDecorations_list,
|
||||
$admin_avatarDecorations_update,
|
||||
$admin_captcha_current,
|
||||
$admin_captcha_save,
|
||||
$admin_deleteAllFilesOfAUser,
|
||||
$admin_unsetUserAvatar,
|
||||
@ -1197,6 +1200,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
||||
$admin_avatarDecorations_delete,
|
||||
$admin_avatarDecorations_list,
|
||||
$admin_avatarDecorations_update,
|
||||
$admin_captcha_current,
|
||||
$admin_captcha_save,
|
||||
$admin_deleteAllFilesOfAUser,
|
||||
$admin_unsetUserAvatar,
|
||||
|
@ -33,6 +33,7 @@ import * as ep___admin_avatarDecorations_create from './endpoints/admin/avatar-d
|
||||
import * as ep___admin_avatarDecorations_delete from './endpoints/admin/avatar-decorations/delete.js';
|
||||
import * as ep___admin_avatarDecorations_list from './endpoints/admin/avatar-decorations/list.js';
|
||||
import * as ep___admin_avatarDecorations_update from './endpoints/admin/avatar-decorations/update.js';
|
||||
import * as ep___admin_captcha_current from './endpoints/admin/captcha/current.js';
|
||||
import * as ep___admin_captcha_save from './endpoints/admin/captcha/save.js';
|
||||
import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js';
|
||||
import * as ep___admin_unsetUserAvatar from './endpoints/admin/unset-user-avatar.js';
|
||||
@ -421,6 +422,7 @@ const eps = [
|
||||
['admin/avatar-decorations/delete', ep___admin_avatarDecorations_delete],
|
||||
['admin/avatar-decorations/list', ep___admin_avatarDecorations_list],
|
||||
['admin/avatar-decorations/update', ep___admin_avatarDecorations_update],
|
||||
['admin/captcha/current', ep___admin_captcha_current],
|
||||
['admin/captcha/save', ep___admin_captcha_save],
|
||||
['admin/delete-all-files-of-a-user', ep___admin_deleteAllFilesOfAUser],
|
||||
['admin/unset-user-avatar', ep___admin_unsetUserAvatar],
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { CaptchaService, supportedCaptchaProviders } from '@/core/CaptchaService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin', 'captcha'],
|
||||
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
|
||||
// 実態はmetaの取得であるため
|
||||
kind: 'read:admin:meta',
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
provider: {
|
||||
type: 'string',
|
||||
enum: supportedCaptchaProviders,
|
||||
},
|
||||
hcaptcha: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
siteKey: { type: 'string', nullable: true },
|
||||
secretKey: { type: 'string', nullable: true },
|
||||
},
|
||||
},
|
||||
mcaptcha: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
siteKey: { type: 'string', nullable: true },
|
||||
secretKey: { type: 'string', nullable: true },
|
||||
instanceUrl: { type: 'string', nullable: true },
|
||||
},
|
||||
},
|
||||
recaptcha: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
siteKey: { type: 'string', nullable: true },
|
||||
secretKey: { type: 'string', nullable: true },
|
||||
},
|
||||
},
|
||||
turnstile: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
siteKey: { type: 'string', nullable: true },
|
||||
secretKey: { type: 'string', nullable: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {} as const;
|
||||
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||
constructor(
|
||||
private captchaService: CaptchaService,
|
||||
) {
|
||||
super(meta, paramDef, async () => {
|
||||
return this.captchaService.get();
|
||||
});
|
||||
}
|
||||
}
|
@ -12,8 +12,7 @@ export const meta = {
|
||||
tags: ['admin', 'captcha'],
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
secure: true,
|
||||
requireAdmin: true,
|
||||
|
||||
// 実態はmetaの更新であるため
|
||||
kind: 'write:admin:meta',
|
||||
|
@ -35,7 +35,10 @@ describe('CaptchaService', () => {
|
||||
provide: HttpRequestService, useFactory: () => ({ send: jest.fn() }),
|
||||
},
|
||||
{
|
||||
provide: MetaService, useFactory: () => ({ update: jest.fn() }),
|
||||
provide: MetaService, useFactory: () => ({
|
||||
fetch: jest.fn(),
|
||||
update: jest.fn(),
|
||||
}),
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
@ -50,6 +53,7 @@ describe('CaptchaService', () => {
|
||||
beforeEach(() => {
|
||||
httpRequestService.send.mockClear();
|
||||
metaService.update.mockClear();
|
||||
metaService.fetch.mockClear();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@ -191,6 +195,123 @@ describe('CaptchaService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('get', () => {
|
||||
function setupMeta(meta: Partial<MiMeta>) {
|
||||
metaService.fetch.mockResolvedValue(meta as MiMeta);
|
||||
}
|
||||
|
||||
test('values', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
hcaptchaSiteKey: 'hcaptcha-sitekey',
|
||||
hcaptchaSecretKey: 'hcaptcha-secret',
|
||||
mcaptchaSitekey: 'mcaptcha-sitekey',
|
||||
mcaptchaSecretKey: 'mcaptcha-secret',
|
||||
mcaptchaInstanceUrl: 'https://localhost',
|
||||
recaptchaSiteKey: 'recaptcha-sitekey',
|
||||
recaptchaSecretKey: 'recaptcha-secret',
|
||||
turnstileSiteKey: 'turnstile-sitekey',
|
||||
turnstileSecretKey: 'turnstile-secret',
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('none');
|
||||
expect(result.hcaptcha.siteKey).toBe('hcaptcha-sitekey');
|
||||
expect(result.hcaptcha.secretKey).toBe('hcaptcha-secret');
|
||||
expect(result.mcaptcha.siteKey).toBe('mcaptcha-sitekey');
|
||||
expect(result.mcaptcha.secretKey).toBe('mcaptcha-secret');
|
||||
expect(result.mcaptcha.instanceUrl).toBe('https://localhost');
|
||||
expect(result.recaptcha.siteKey).toBe('recaptcha-sitekey');
|
||||
expect(result.recaptcha.secretKey).toBe('recaptcha-secret');
|
||||
expect(result.turnstile.siteKey).toBe('turnstile-sitekey');
|
||||
expect(result.turnstile.secretKey).toBe('turnstile-secret');
|
||||
});
|
||||
|
||||
describe('provider', () => {
|
||||
test('none', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('none');
|
||||
});
|
||||
|
||||
test('hcaptcha', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: true,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('hcaptcha');
|
||||
});
|
||||
|
||||
test('mcaptcha', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: true,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('mcaptcha');
|
||||
});
|
||||
|
||||
test('recaptcha', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: true,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('recaptcha');
|
||||
});
|
||||
|
||||
test('turnstile', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: true,
|
||||
enableTestcaptcha: false,
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('turnstile');
|
||||
});
|
||||
|
||||
test('testcaptcha', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: true,
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('testcaptcha');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('save', () => {
|
||||
const host = 'https://localhost';
|
||||
|
||||
|
@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkRadios v-model="botProtectionForm.state.provider">
|
||||
<option :value="null">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option>
|
||||
<option value="none">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option>
|
||||
<option value="hcaptcha">hCaptcha</option>
|
||||
<option value="mcaptcha">mCaptcha</option>
|
||||
<option value="recaptcha">reCAPTCHA</option>
|
||||
@ -158,12 +158,69 @@ import MkInfo from '@/components/MkInfo.vue';
|
||||
|
||||
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
|
||||
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
|
||||
const captchaResult = ref<string | null>(null);
|
||||
|
||||
const meta = await misskeyApi('admin/captcha/current');
|
||||
const botProtectionForm = useForm({
|
||||
provider: meta.provider,
|
||||
hcaptchaSiteKey: meta.hcaptcha.siteKey,
|
||||
hcaptchaSecretKey: meta.hcaptcha.secretKey,
|
||||
mcaptchaSiteKey: meta.mcaptcha.siteKey,
|
||||
mcaptchaSecretKey: meta.mcaptcha.secretKey,
|
||||
mcaptchaInstanceUrl: meta.mcaptcha.instanceUrl,
|
||||
recaptchaSiteKey: meta.recaptcha.siteKey,
|
||||
recaptchaSecretKey: meta.recaptcha.secretKey,
|
||||
turnstileSiteKey: meta.turnstile.siteKey,
|
||||
turnstileSecretKey: meta.turnstile.secretKey,
|
||||
}, async (state) => {
|
||||
const provider = state.provider;
|
||||
|
||||
const sitekey = provider === 'hcaptcha'
|
||||
? state.hcaptchaSiteKey
|
||||
: provider === 'mcaptcha'
|
||||
? state.mcaptchaSiteKey
|
||||
: provider === 'recaptcha'
|
||||
? state.recaptchaSiteKey
|
||||
: provider === 'turnstile'
|
||||
? state.turnstileSiteKey
|
||||
: null;
|
||||
const secret = provider === 'hcaptcha'
|
||||
? state.hcaptchaSecretKey
|
||||
: provider === 'mcaptcha'
|
||||
? state.mcaptchaSecretKey
|
||||
: provider === 'recaptcha'
|
||||
? state.recaptchaSecretKey
|
||||
: provider === 'turnstile'
|
||||
? state.turnstileSecretKey
|
||||
: null;
|
||||
|
||||
if (provider === 'none') {
|
||||
await os.apiWithDialog(
|
||||
'admin/captcha/save',
|
||||
{ provider: provider as Misskey.entities.AdminCaptchaSaveRequest['provider'] },
|
||||
);
|
||||
} else {
|
||||
await os.apiWithDialog(
|
||||
'admin/captcha/save',
|
||||
{
|
||||
provider: provider as Misskey.entities.AdminCaptchaSaveRequest['provider'],
|
||||
sitekey: sitekey,
|
||||
secret: secret,
|
||||
instanceUrl: state.mcaptchaInstanceUrl,
|
||||
captchaResult: captchaResult.value,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
await fetchInstance(true);
|
||||
});
|
||||
|
||||
watch(botProtectionForm.state, () => {
|
||||
captchaResult.value = null;
|
||||
});
|
||||
|
||||
const canSaving = computed((): boolean => {
|
||||
return (botProtectionForm.state.provider === null) ||
|
||||
return (botProtectionForm.state.provider === 'none') ||
|
||||
(botProtectionForm.state.provider === 'hcaptcha' && !!captchaResult.value) ||
|
||||
(botProtectionForm.state.provider === 'mcaptcha' && !!captchaResult.value) ||
|
||||
(botProtectionForm.state.provider === 'recaptcha' && !!captchaResult.value) ||
|
||||
@ -171,68 +228,6 @@ const canSaving = computed((): boolean => {
|
||||
(botProtectionForm.state.provider === 'testcaptcha' && !!captchaResult.value);
|
||||
});
|
||||
|
||||
const botProtectionForm = useForm({
|
||||
provider: meta.enableHcaptcha
|
||||
? 'hcaptcha'
|
||||
: meta.enableRecaptcha
|
||||
? 'recaptcha'
|
||||
: meta.enableTurnstile
|
||||
? 'turnstile'
|
||||
: meta.enableMcaptcha
|
||||
? 'mcaptcha'
|
||||
: meta.enableTestcaptcha
|
||||
? 'testcaptcha'
|
||||
: null,
|
||||
hcaptchaSiteKey: meta.hcaptchaSiteKey,
|
||||
hcaptchaSecretKey: meta.hcaptchaSecretKey,
|
||||
mcaptchaSiteKey: meta.mcaptchaSiteKey,
|
||||
mcaptchaSecretKey: meta.mcaptchaSecretKey,
|
||||
mcaptchaInstanceUrl: meta.mcaptchaInstanceUrl,
|
||||
recaptchaSiteKey: meta.recaptchaSiteKey,
|
||||
recaptchaSecretKey: meta.recaptchaSecretKey,
|
||||
turnstileSiteKey: meta.turnstileSiteKey,
|
||||
turnstileSecretKey: meta.turnstileSecretKey,
|
||||
}, async (state) => {
|
||||
const provider = botProtectionForm.state.provider;
|
||||
|
||||
const sitekey = provider === 'hcaptcha'
|
||||
? botProtectionForm.state.hcaptchaSiteKey
|
||||
: provider === 'mcaptcha'
|
||||
? botProtectionForm.state.mcaptchaSiteKey
|
||||
: provider === 'recaptcha'
|
||||
? botProtectionForm.state.recaptchaSiteKey
|
||||
: provider === 'turnstile'
|
||||
? botProtectionForm.state.turnstileSiteKey
|
||||
: null;
|
||||
const secret = provider === 'hcaptcha'
|
||||
? botProtectionForm.state.hcaptchaSecretKey
|
||||
: provider === 'mcaptcha'
|
||||
? botProtectionForm.state.mcaptchaSecretKey
|
||||
: provider === 'recaptcha'
|
||||
? botProtectionForm.state.recaptchaSecretKey
|
||||
: provider === 'turnstile'
|
||||
? botProtectionForm.state.turnstileSecretKey
|
||||
: null;
|
||||
|
||||
if (captchaResult.value) {
|
||||
await os.apiWithDialog(
|
||||
'admin/captcha/save',
|
||||
{
|
||||
provider: provider as Misskey.entities.AdminCaptchaSaveRequest['provider'],
|
||||
sitekey: sitekey,
|
||||
secret: secret,
|
||||
instanceUrl: botProtectionForm.state.mcaptchaInstanceUrl,
|
||||
captchaResult: captchaResult.value,
|
||||
},
|
||||
);
|
||||
|
||||
await fetchInstance(true);
|
||||
}
|
||||
});
|
||||
|
||||
watch(botProtectionForm.state, () => {
|
||||
captchaResult.value = null;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
@ -136,6 +136,9 @@ type AdminAvatarDecorationsListResponse = operations['admin___avatar-decorations
|
||||
// @public (undocumented)
|
||||
type AdminAvatarDecorationsUpdateRequest = operations['admin___avatar-decorations___update']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminCaptchaCurrentResponse = operations['admin___captcha___current']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminCaptchaSaveRequest = operations['admin___captcha___save']['requestBody']['content']['application/json'];
|
||||
|
||||
@ -1264,6 +1267,7 @@ declare namespace entities {
|
||||
AdminAvatarDecorationsListRequest,
|
||||
AdminAvatarDecorationsListResponse,
|
||||
AdminAvatarDecorationsUpdateRequest,
|
||||
AdminCaptchaCurrentResponse,
|
||||
AdminCaptchaSaveRequest,
|
||||
AdminDeleteAllFilesOfAUserRequest,
|
||||
AdminUnsetUserAvatarRequest,
|
||||
|
@ -253,8 +253,18 @@ declare module '../api.js' {
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
* **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
|
||||
* **Credential required**: *Yes* / **Permission**: *read:admin:captcha*
|
||||
* **Credential required**: *Yes* / **Permission**: *read:admin:meta*
|
||||
*/
|
||||
request<E extends 'admin/captcha/current', P extends Endpoints[E]['req']>(
|
||||
endpoint: E,
|
||||
params: P,
|
||||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:admin:meta*
|
||||
*/
|
||||
request<E extends 'admin/captcha/save', P extends Endpoints[E]['req']>(
|
||||
endpoint: E,
|
||||
|
@ -36,6 +36,7 @@ import type {
|
||||
AdminAvatarDecorationsListRequest,
|
||||
AdminAvatarDecorationsListResponse,
|
||||
AdminAvatarDecorationsUpdateRequest,
|
||||
AdminCaptchaCurrentResponse,
|
||||
AdminCaptchaSaveRequest,
|
||||
AdminDeleteAllFilesOfAUserRequest,
|
||||
AdminUnsetUserAvatarRequest,
|
||||
@ -605,6 +606,7 @@ export type Endpoints = {
|
||||
'admin/avatar-decorations/delete': { req: AdminAvatarDecorationsDeleteRequest; res: EmptyResponse };
|
||||
'admin/avatar-decorations/list': { req: AdminAvatarDecorationsListRequest; res: AdminAvatarDecorationsListResponse };
|
||||
'admin/avatar-decorations/update': { req: AdminAvatarDecorationsUpdateRequest; res: EmptyResponse };
|
||||
'admin/captcha/current': { req: EmptyRequest; res: AdminCaptchaCurrentResponse };
|
||||
'admin/captcha/save': { req: AdminCaptchaSaveRequest; res: EmptyResponse };
|
||||
'admin/delete-all-files-of-a-user': { req: AdminDeleteAllFilesOfAUserRequest; res: EmptyResponse };
|
||||
'admin/unset-user-avatar': { req: AdminUnsetUserAvatarRequest; res: EmptyResponse };
|
||||
|
@ -39,6 +39,7 @@ export type AdminAvatarDecorationsDeleteRequest = operations['admin___avatar-dec
|
||||
export type AdminAvatarDecorationsListRequest = operations['admin___avatar-decorations___list']['requestBody']['content']['application/json'];
|
||||
export type AdminAvatarDecorationsListResponse = operations['admin___avatar-decorations___list']['responses']['200']['content']['application/json'];
|
||||
export type AdminAvatarDecorationsUpdateRequest = operations['admin___avatar-decorations___update']['requestBody']['content']['application/json'];
|
||||
export type AdminCaptchaCurrentResponse = operations['admin___captcha___current']['responses']['200']['content']['application/json'];
|
||||
export type AdminCaptchaSaveRequest = operations['admin___captcha___save']['requestBody']['content']['application/json'];
|
||||
export type AdminDeleteAllFilesOfAUserRequest = operations['admin___delete-all-files-of-a-user']['requestBody']['content']['application/json'];
|
||||
export type AdminUnsetUserAvatarRequest = operations['admin___unset-user-avatar']['requestBody']['content']['application/json'];
|
||||
|
@ -215,13 +215,21 @@ export type paths = {
|
||||
*/
|
||||
post: operations['admin___avatar-decorations___update'];
|
||||
};
|
||||
'/admin/captcha/current': {
|
||||
/**
|
||||
* admin/captcha/current
|
||||
* @description No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *read:admin:meta*
|
||||
*/
|
||||
post: operations['admin___captcha___current'];
|
||||
};
|
||||
'/admin/captcha/save': {
|
||||
/**
|
||||
* admin/captcha/save
|
||||
* @description No description provided.
|
||||
*
|
||||
* **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
|
||||
* **Credential required**: *Yes* / **Permission**: *read:admin:captcha*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:admin:meta*
|
||||
*/
|
||||
post: operations['admin___captcha___save'];
|
||||
};
|
||||
@ -6574,12 +6582,77 @@ export type operations = {
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* admin/captcha/current
|
||||
* @description No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *read:admin:meta*
|
||||
*/
|
||||
admin___captcha___current: {
|
||||
responses: {
|
||||
/** @description OK (with results) */
|
||||
200: {
|
||||
content: {
|
||||
'application/json': {
|
||||
/** @enum {string} */
|
||||
provider: 'none' | 'hcaptcha' | 'mcaptcha' | 'recaptcha' | 'turnstile' | 'testcaptcha';
|
||||
hcaptcha: {
|
||||
siteKey: string | null;
|
||||
secretKey: string | null;
|
||||
};
|
||||
mcaptcha: {
|
||||
siteKey: string | null;
|
||||
secretKey: string | null;
|
||||
instanceUrl: string | null;
|
||||
};
|
||||
recaptcha: {
|
||||
siteKey: string | null;
|
||||
secretKey: string | null;
|
||||
};
|
||||
turnstile: {
|
||||
siteKey: string | null;
|
||||
secretKey: string | null;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/** @description Client error */
|
||||
400: {
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description Authentication error */
|
||||
401: {
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description Forbidden error */
|
||||
403: {
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description I'm Ai */
|
||||
418: {
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description Internal server error */
|
||||
500: {
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* admin/captcha/save
|
||||
* @description No description provided.
|
||||
*
|
||||
* **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
|
||||
* **Credential required**: *Yes* / **Permission**: *read:admin:captcha*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:admin:meta*
|
||||
*/
|
||||
admin___captcha___save: {
|
||||
requestBody: {
|
||||
|
Loading…
Reference in New Issue
Block a user