mirror of
https://github.com/koreanbots/core.git
synced 2025-12-15 14:10:22 +00:00
feat: added captcha at addbot
This commit is contained in:
parent
c90afdb75d
commit
0a7edca2d8
@ -1,4 +1,5 @@
|
|||||||
import HCaptcha from '@hcaptcha/react-hcaptcha'
|
import HCaptcha from '@hcaptcha/react-hcaptcha'
|
||||||
|
import { Ref } from 'react'
|
||||||
|
|
||||||
const Captcha = ({ dark, onVerify }:CaptchaProps):JSX.Element => {
|
const Captcha = ({ dark, onVerify }:CaptchaProps):JSX.Element => {
|
||||||
return <HCaptcha sitekey='43e556b4-cc90-494f-b100-378b906bb736' theme={dark ? 'dark' : 'light'} onVerify={onVerify}/>
|
return <HCaptcha sitekey='43e556b4-cc90-494f-b100-378b906bb736' theme={dark ? 'dark' : 'light'} onVerify={onVerify}/>
|
||||||
@ -7,6 +8,7 @@ const Captcha = ({ dark, onVerify }:CaptchaProps):JSX.Element => {
|
|||||||
interface CaptchaProps {
|
interface CaptchaProps {
|
||||||
dark: boolean
|
dark: boolean
|
||||||
onVerify(token: string, eKey?: string): void
|
onVerify(token: string, eKey?: string): void
|
||||||
|
ref?: Ref<HCaptcha>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Captcha
|
export default Captcha
|
||||||
@ -1,9 +1,10 @@
|
|||||||
import { NextPage, NextPageContext } from 'next'
|
import { NextPage, NextPageContext } from 'next'
|
||||||
import { useState } from 'react'
|
import { useRef, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { Form, Formik } from 'formik'
|
import { Form, Formik } from 'formik'
|
||||||
|
import HCaptcha from '@hcaptcha/react-hcaptcha'
|
||||||
|
|
||||||
import { get } from '@utils/Query'
|
import { get } from '@utils/Query'
|
||||||
import { cleanObject, parseCookie, redirectTo } from '@utils/Tools'
|
import { cleanObject, parseCookie, redirectTo } from '@utils/Tools'
|
||||||
@ -25,18 +26,44 @@ const Selects = dynamic(() => import('@components/Form/Selects'))
|
|||||||
const Button = dynamic(() => import('@components/Button'))
|
const Button = dynamic(() => import('@components/Button'))
|
||||||
const Container = dynamic(() => import('@components/Container'))
|
const Container = dynamic(() => import('@components/Container'))
|
||||||
const Message = dynamic(() => import('@components/Message'))
|
const Message = dynamic(() => import('@components/Message'))
|
||||||
|
const Captcha = dynamic(() => import('@components/Captcha'))
|
||||||
const SEO = dynamic(() => import('@components/SEO'))
|
const SEO = dynamic(() => import('@components/SEO'))
|
||||||
|
|
||||||
const AddBot:NextPage<AddBotProps> = ({ logged, user, csrfToken, theme }) => {
|
const AddBot:NextPage<AddBotProps> = ({ logged, user, csrfToken, theme }) => {
|
||||||
const [ data, setData ] = useState<ResponseProps<SubmittedBot>>(null)
|
const [ data, setData ] = useState<ResponseProps<SubmittedBot>>(null)
|
||||||
|
const [ captcha, setCaptcha ] = useState(false)
|
||||||
|
const captchaRef = useRef<HCaptcha>()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const initialValues: AddBotSubmit = {
|
||||||
|
agree: false,
|
||||||
|
id: '',
|
||||||
|
prefix: '',
|
||||||
|
library: '',
|
||||||
|
category: [],
|
||||||
|
intro: '',
|
||||||
|
desc: `<!-- 이 설명을 지우시고 원하시는 설명을 적으셔도 좋습니다! -->
|
||||||
|
# 봇이름
|
||||||
|
자신의 봇을 자유롭게 표현해보세요!
|
||||||
|
|
||||||
|
## ✏️ 소개
|
||||||
|
|
||||||
|
무엇이 목적인 봇인가요?
|
||||||
|
|
||||||
|
## 🛠️ 기능
|
||||||
|
|
||||||
|
- 어떤
|
||||||
|
- 기능
|
||||||
|
- 있나요?`,
|
||||||
|
_csrf: csrfToken,
|
||||||
|
_captcha: 'captcha'
|
||||||
|
}
|
||||||
function toLogin() {
|
function toLogin() {
|
||||||
localStorage.redirectTo = window.location.href
|
localStorage.redirectTo = window.location.href
|
||||||
redirectTo(router, 'login')
|
redirectTo(router, 'login')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitBot(value: AddBotSubmit) {
|
async function submitBot(value: AddBotSubmit, token: string) {
|
||||||
const res = await Fetch<SubmittedBot>(`/bots/${value.id}`, { method: 'POST', body: JSON.stringify(cleanObject<AddBotSubmit>(value)) })
|
const res = await Fetch<SubmittedBot>(`/bots/${value.id}`, { method: 'POST', body: JSON.stringify(cleanObject<AddBotSubmit>({ ...value, _captcha: token})) })
|
||||||
setData(res)
|
setData(res)
|
||||||
}
|
}
|
||||||
if(!logged) {
|
if(!logged) {
|
||||||
@ -61,31 +88,10 @@ const AddBot:NextPage<AddBotProps> = ({ logged, user, csrfToken, theme }) => {
|
|||||||
|
|
||||||
</Message> : <></>
|
</Message> : <></>
|
||||||
}
|
}
|
||||||
<Formik initialValues={{
|
<Formik initialValues={initialValues}
|
||||||
agree: false,
|
validationSchema={AddBotSubmitSchema}
|
||||||
id: '',
|
onSubmit={() => setCaptcha(true)}>
|
||||||
prefix: '',
|
{({ errors, touched, values, isValid, setFieldTouched, setFieldValue }) => (
|
||||||
library: '',
|
|
||||||
category: [],
|
|
||||||
intro: '',
|
|
||||||
desc: `<!-- 이 설명을 지우시고 원하시는 설명을 적으셔도 좋습니다! -->
|
|
||||||
# 봇이름
|
|
||||||
자신의 봇을 자유롭게 표현해보세요!
|
|
||||||
|
|
||||||
## ✏️ 소개
|
|
||||||
|
|
||||||
무엇이 목적인 봇인가요?
|
|
||||||
|
|
||||||
## 🛠️ 기능
|
|
||||||
|
|
||||||
- 어떤
|
|
||||||
- 기능
|
|
||||||
- 있나요?`,
|
|
||||||
_csrf: csrfToken
|
|
||||||
}}
|
|
||||||
validationSchema={AddBotSubmitSchema}
|
|
||||||
onSubmit={submitBot}>
|
|
||||||
{({ errors, touched, values, setFieldTouched, setFieldValue }) => (
|
|
||||||
<Form>
|
<Form>
|
||||||
<div className='py-3'>
|
<div className='py-3'>
|
||||||
<Message type='warning'>
|
<Message type='warning'>
|
||||||
@ -113,7 +119,6 @@ const AddBot:NextPage<AddBotProps> = ({ logged, user, csrfToken, theme }) => {
|
|||||||
</div>
|
</div>
|
||||||
</Label>
|
</Label>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<Label For='id' label='봇 ID' labelDesc='봇의 클라이언트 ID를 의미합니다.' error={errors.id && touched.id ? errors.id : null} short required>
|
<Label For='id' label='봇 ID' labelDesc='봇의 클라이언트 ID를 의미합니다.' error={errors.id && touched.id ? errors.id : null} short required>
|
||||||
<Input name='id' placeholder='653534001742741552' />
|
<Input name='id' placeholder='653534001742741552' />
|
||||||
</Label>
|
</Label>
|
||||||
@ -169,11 +174,20 @@ const AddBot:NextPage<AddBotProps> = ({ logged, user, csrfToken, theme }) => {
|
|||||||
</Segment>
|
</Segment>
|
||||||
</Label>
|
</Label>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Button type='submit' onClick={() => window.scrollTo({ top: 0 })}>
|
{
|
||||||
<>
|
captcha ? <Captcha ref={captchaRef} dark={theme === 'dark'} onVerify={(token) => {
|
||||||
<i className='far fa-paper-plane'/> 제출
|
submitBot(values, token)
|
||||||
</>
|
window.scrollTo({ top: 0 })
|
||||||
</Button>
|
setCaptcha(false)
|
||||||
|
captchaRef?.current?.resetCaptcha()
|
||||||
|
}} /> : <Button type='submit' onClick={() => {
|
||||||
|
if(!isValid) window.scrollTo({ top: 0 })
|
||||||
|
} }>
|
||||||
|
<>
|
||||||
|
<i className='far fa-paper-plane'/> 제출
|
||||||
|
</>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { NextApiRequest } from 'next'
|
import { NextApiRequest } from 'next'
|
||||||
import rateLimit from 'express-rate-limit'
|
import rateLimit from 'express-rate-limit'
|
||||||
|
|
||||||
import { get, put, update } from '@utils/Query'
|
import { CaptchaVerify, get, put, update } from '@utils/Query'
|
||||||
import ResponseWrapper from '@utils/ResponseWrapper'
|
import ResponseWrapper from '@utils/ResponseWrapper'
|
||||||
import { checkToken } from '@utils/Csrf'
|
import { checkToken } from '@utils/Csrf'
|
||||||
import { AddBotSubmit, AddBotSubmitSchema, ManageBot, ManageBotSchema } from '@utils/Yup'
|
import { AddBotSubmit, AddBotSubmitSchema, ManageBot, ManageBotSchema } from '@utils/Yup'
|
||||||
@ -41,6 +41,8 @@ const Bots = RequestHandler()
|
|||||||
if (!validated) return
|
if (!validated) return
|
||||||
if (validated.id !== req.query.id)
|
if (validated.id !== req.query.id)
|
||||||
return ResponseWrapper(res, { code: 400, errors: ['요청 주소와 Body의 정보가 다릅니다.'] })
|
return ResponseWrapper(res, { code: 400, errors: ['요청 주소와 Body의 정보가 다릅니다.'] })
|
||||||
|
const captcha = await CaptchaVerify(validated._captcha)
|
||||||
|
if(!captcha) return ResponseWrapper(res, { code: 400, message: '캡챠 검증에 실패하였습니다.' })
|
||||||
const result = await put.submitBot(user, validated)
|
const result = await put.submitBot(user, validated)
|
||||||
if (result === 1)
|
if (result === 1)
|
||||||
return ResponseWrapper(res, {
|
return ResponseWrapper(res, {
|
||||||
|
|||||||
@ -185,6 +185,9 @@ export const SpecialEndPoints = {
|
|||||||
Github: {
|
Github: {
|
||||||
Token: (clientID: string, clientSecret: string, code: string) => `https://github.com/login/oauth/access_token?client_id=${clientID}&client_secret=${clientSecret}&code=${code}`,
|
Token: (clientID: string, clientSecret: string, code: string) => `https://github.com/login/oauth/access_token?client_id=${clientID}&client_secret=${clientSecret}&code=${code}`,
|
||||||
Me: 'https://api.github.com/user'
|
Me: 'https://api.github.com/user'
|
||||||
|
},
|
||||||
|
HCaptcha: {
|
||||||
|
Verify: 'https://hcaptcha.com/siteverify'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,12 +4,12 @@ import DataLoader from 'dataloader'
|
|||||||
import { User as DiscordUser } from 'discord.js'
|
import { User as DiscordUser } from 'discord.js'
|
||||||
|
|
||||||
import { Bot, User, ListType, BotList, TokenRegister, BotFlags, DiscordUserFlags, SubmittedBot } from '@types'
|
import { Bot, User, ListType, BotList, TokenRegister, BotFlags, DiscordUserFlags, SubmittedBot } from '@types'
|
||||||
import { categories } from './Constants'
|
import { categories, SpecialEndPoints } from './Constants'
|
||||||
|
|
||||||
import knex from './Knex'
|
import knex from './Knex'
|
||||||
import { DiscordBot, getMainGuild } from './DiscordBot'
|
import { DiscordBot, getMainGuild } from './DiscordBot'
|
||||||
import { sign, verify } from './Jwt'
|
import { sign, verify } from './Jwt'
|
||||||
import { serialize } from './Tools'
|
import { formData, serialize } from './Tools'
|
||||||
import { AddBotSubmit, ManageBot } from './Yup'
|
import { AddBotSubmit, ManageBot } from './Yup'
|
||||||
|
|
||||||
export const imageRateLimit = new TLRU<unknown, number>({ maxAgeMs: 60000 })
|
export const imageRateLimit = new TLRU<unknown, number>({ maxAgeMs: 60000 })
|
||||||
@ -321,6 +321,23 @@ async function addRequest(ip: string, map: TLRU<unknown, number>) {
|
|||||||
map.set(ip, map.get(ip) + 1)
|
map.set(ip, map.get(ip) + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function CaptchaVerify(response: string): Promise<boolean> {
|
||||||
|
const res:{ success: boolean } = await fetch(SpecialEndPoints.HCaptcha.Verify, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
body: formData({
|
||||||
|
response,
|
||||||
|
secret: process.env.HCAPTCHA_KEY
|
||||||
|
})
|
||||||
|
}).then(r=> r.json())
|
||||||
|
|
||||||
|
console.log(res)
|
||||||
|
|
||||||
|
return res.success
|
||||||
|
}
|
||||||
|
|
||||||
export const get = {
|
export const get = {
|
||||||
discord: {
|
discord: {
|
||||||
user: new DataLoader(
|
user: new DataLoader(
|
||||||
|
|||||||
@ -167,6 +167,7 @@ export const AddBotSubmitSchema: Yup.SchemaOf<AddBotSubmit> = Yup.object({
|
|||||||
.max(1500, '봇 설명은 최대 1500자여야합니다.')
|
.max(1500, '봇 설명은 최대 1500자여야합니다.')
|
||||||
.required('봇 설명은 필수 항목입니다.'),
|
.required('봇 설명은 필수 항목입니다.'),
|
||||||
_csrf: Yup.string().required(),
|
_csrf: Yup.string().required(),
|
||||||
|
_captcha: Yup.string().required()
|
||||||
})
|
})
|
||||||
|
|
||||||
export interface AddBotSubmit {
|
export interface AddBotSubmit {
|
||||||
@ -182,6 +183,7 @@ export interface AddBotSubmit {
|
|||||||
intro: string
|
intro: string
|
||||||
desc: string
|
desc: string
|
||||||
_csrf: string
|
_csrf: string
|
||||||
|
_captcha?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BotStatUpdateSchema: Yup.SchemaOf<BotStatUpdate> = Yup.object({
|
export const BotStatUpdateSchema: Yup.SchemaOf<BotStatUpdate> = Yup.object({
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user