diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 68c333a7cf..72db473de5 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -421,7 +421,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/test', ep___admin_captcha_save], + ['admin/captcha/save', ep___admin_captcha_save], ['admin/delete-all-files-of-a-user', ep___admin_deleteAllFilesOfAUser], ['admin/unset-user-avatar', ep___admin_unsetUserAvatar], ['admin/unset-user-banner', ep___admin_unsetUserBanner], diff --git a/packages/backend/src/server/api/endpoints/admin/captcha/save.ts b/packages/backend/src/server/api/endpoints/admin/captcha/save.ts index ad41e0b361..d3a71e32f3 100644 --- a/packages/backend/src/server/api/endpoints/admin/captcha/save.ts +++ b/packages/backend/src/server/api/endpoints/admin/captcha/save.ts @@ -5,7 +5,8 @@ import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { CaptchaService, supportedCaptchaProviders } from '@/core/CaptchaService.js'; +import { captchaErrorCodes, CaptchaService, supportedCaptchaProviders } from '@/core/CaptchaService.js'; +import { ApiError } from '@/server/api/error.js'; export const meta = { tags: ['admin', 'captcha'], @@ -16,25 +17,42 @@ export const meta = { kind: 'read:admin:captcha', - res: { - type: 'object', - properties: { - success: { - type: 'boolean', - }, - error: { - type: 'object', - nullable: true, - optional: false, - properties: { - code: { - type: 'string', - }, - message: { - type: 'string', - }, - }, - }, + errors: { + invalidProvider: { + message: 'Invalid provider.', + code: 'INVALID_PROVIDER', + id: '14BF7AE1-80CC-4363-ACB2-4FD61D086AF0', + httpStatusCode: 400, + }, + invalidParameters: { + message: 'Invalid parameters.', + code: 'INVALID_PARAMETERS', + id: '26654194-410E-44E2-B42E-460FF6F92476', + httpStatusCode: 400, + }, + noResponseProvided: { + message: 'No response provided.', + code: 'NO_RESPONSE_PROVIDED', + id: '40ACBBA8-0937-41FB-BB3F-474514D40AFE', + httpStatusCode: 400, + }, + requestFailed: { + message: 'Request failed.', + code: 'REQUEST_FAILED', + id: '0F4FE2F1-2C15-4D6E-B714-EFBFCDE231CD', + httpStatusCode: 500, + }, + verificationFailed: { + message: 'Verification failed.', + code: 'VERIFICATION_FAILED', + id: 'C41C067F-24F3-4150-84B2-B5A3AE8C2214', + httpStatusCode: 400, + }, + unknown: { + message: 'unknown', + code: 'UNKNOWN', + id: 'F868D509-E257-42A9-99C1-42614B031A97', + httpStatusCode: 500, }, }, } as const; @@ -68,22 +86,43 @@ export default class extends Endpoint { // eslint- private captchaService: CaptchaService, ) { super(meta, paramDef, async (ps) => { - const result = await this.captchaService.save(ps.provider, ps.captchaResult, { + const result = await this.captchaService.save(ps.provider, { sitekey: ps.sitekey, secret: ps.secret, instanceUrl: ps.instanceUrl, + captchaResult: ps.captchaResult, }); - if (result.success) { - return { success: true, error: null }; - } else { - return { - success: false, - error: { - code: result.error.code.toString(), - message: result.error.message, - }, - }; + if (!result.success) { + switch (result.error.code) { + case captchaErrorCodes.invalidProvider: + throw new ApiError({ + ...meta.errors.invalidProvider, + message: result.error.message, + }); + case captchaErrorCodes.invalidParameters: + throw new ApiError({ + ...meta.errors.invalidParameters, + message: result.error.message, + }); + case captchaErrorCodes.noResponseProvided: + throw new ApiError({ + ...meta.errors.noResponseProvided, + message: result.error.message, + }); + case captchaErrorCodes.requestFailed: + throw new ApiError({ + ...meta.errors.requestFailed, + message: result.error.message, + }); + case captchaErrorCodes.verificationFailed: + throw new ApiError({ + ...meta.errors.verificationFailed, + message: result.error.message, + }); + default: + throw new ApiError(meta.errors.unknown); + } } }); } diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue index 3e6810240b..aa41edd219 100644 --- a/packages/frontend/src/pages/admin/bot-protection.vue +++ b/packages/frontend/src/pages/admin/bot-protection.vue @@ -142,10 +142,6 @@ SPDX-License-Identifier: AGPL-3.0-only - - - {{ verifyErrorText }} - @@ -170,16 +166,14 @@ const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue' const meta = await misskeyApi('admin/meta'); const captchaResult = ref(null); -const verifyResult = ref(false); -const verifyErrorText = ref(null); const canSaving = computed((): boolean => { return (botProtectionForm.state.provider === null) || - (botProtectionForm.state.provider === 'hcaptcha' && verifyResult.value) || - (botProtectionForm.state.provider === 'mcaptcha' && verifyResult.value) || - (botProtectionForm.state.provider === 'recaptcha' && verifyResult.value) || - (botProtectionForm.state.provider === 'turnstile' && verifyResult.value) || - (botProtectionForm.state.provider === 'testcaptcha' && verifyResult.value); + (botProtectionForm.state.provider === 'hcaptcha' && !!captchaResult.value) || + (botProtectionForm.state.provider === 'mcaptcha' && !!captchaResult.value) || + (botProtectionForm.state.provider === 'recaptcha' && !!captchaResult.value) || + (botProtectionForm.state.provider === 'turnstile' && !!captchaResult.value) || + (botProtectionForm.state.provider === 'testcaptcha' && !!captchaResult.value); }); const botProtectionForm = useForm({ @@ -204,36 +198,6 @@ const botProtectionForm = useForm({ turnstileSiteKey: meta.turnstileSiteKey, turnstileSecretKey: meta.turnstileSecretKey, }, async (state) => { - await os.apiWithDialog('admin/update-meta', { - enableHcaptcha: state.provider === 'hcaptcha', - hcaptchaSiteKey: state.hcaptchaSiteKey, - hcaptchaSecretKey: state.hcaptchaSecretKey, - enableMcaptcha: state.provider === 'mcaptcha', - mcaptchaSiteKey: state.mcaptchaSiteKey, - mcaptchaSecretKey: state.mcaptchaSecretKey, - mcaptchaInstanceUrl: state.mcaptchaInstanceUrl, - enableRecaptcha: state.provider === 'recaptcha', - recaptchaSiteKey: state.recaptchaSiteKey, - recaptchaSecretKey: state.recaptchaSecretKey, - enableTurnstile: state.provider === 'turnstile', - turnstileSiteKey: state.turnstileSiteKey, - turnstileSecretKey: state.turnstileSecretKey, - enableTestcaptcha: state.provider === 'testcaptcha', - }); - fetchInstance(true); -}); - -watch(botProtectionForm.state, () => { - captchaResult.value = null; - if (botProtectionForm.state.provider === null) { - verifyResult.value = true; - } else { - verifyResult.value = false; - verifyErrorText.value = null; - } -}); - -watch(captchaResult, async () => { const provider = botProtectionForm.state.provider; const sitekey = provider === 'hcaptcha' @@ -256,22 +220,24 @@ watch(captchaResult, async () => { : null; if (captchaResult.value) { - const result = await misskeyApi('admin/captcha/test', { - provider: provider as Misskey.entities.AdminCaptchaTestRequest['provider'], - sitekey: sitekey, - secret: secret, - instanceUrl: botProtectionForm.state.mcaptchaInstanceUrl, - captchaResult: 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, + }, + ); - verifyResult.value = result.success; - verifyErrorText.value = result.error - ? result.error.message - : null; - } else { - verifyResult.value = false; + await fetchInstance(true); } }); + +watch(botProtectionForm.state, () => { + captchaResult.value = null; +});