feat: implement bot server/shard limit exceed form on bot editpge

This commit is contained in:
soyoka 2025-11-06 16:13:14 +09:00
parent 15d9bed12c
commit 475ed926b8
3 changed files with 162 additions and 2 deletions

View File

@ -0,0 +1,74 @@
import { NextApiRequest } from 'next'
import { KoreanbotsEndPoints } from '@utils/Constants'
import { checkToken } from '@utils/Csrf'
import { discordLog } from '@utils/DiscordBot'
import { CaptchaVerify, get, update } from '@utils/Query'
import RequestHandler from '@utils/RequestHandler'
import ResponseWrapper from '@utils/ResponseWrapper'
import { checkUserFlag, makeDiscordCodeblock } from '@utils/Tools'
import { ExceedLimit, ExceedLimitScehma } from '@utils/Yup'
import { EmbedBuilder } from 'discord.js'
const BotLimit = RequestHandler().patch(async (req: PostApiRequest, res) => {
const user = await get.Authorization(req.cookies.token)
if (!user) return ResponseWrapper(res, { code: 401 })
const userinfo = await get.user.load(user)
const bot = await get.bot.load(req.query.id)
if (!bot) return ResponseWrapper(res, { code: 404 })
if (!checkUserFlag(userinfo.flags, 'staff')) return ResponseWrapper(res, { code: 403 })
const validated = await ExceedLimitScehma.validate(req.body, { abortEarly: false })
.then((el) => el)
.catch((e) => {
ResponseWrapper(res, { code: 400, errors: e.errors })
return null
})
if (!validated) return
const csrfValidated = checkToken(req, res, validated._csrf)
if (!csrfValidated) return
const captcha = await CaptchaVerify(validated._captcha)
if (!captcha) return
const response = await update.updateServer(
bot.id,
validated.servers,
validated.shards == 0 ? undefined : validated.shards,
true
)
console.log(response)
get.user.clear(user)
await discordLog(
'BOT/EXCEED_LIMIT',
userinfo.id,
new EmbedBuilder().setDescription(
`${bot.name} - <@${bot.id}> ([${bot.id}](${KoreanbotsEndPoints.URL.bot(bot.id)}))`
),
null,
makeDiscordCodeblock(
`${bot.servers > validated.servers ? '-' : '+'} ${bot.servers} -> ${
validated.servers
} (${bot.servers > validated.servers ? '▼' : '▲'}${Math.abs(validated.servers - bot.servers)})
+ ${
bot.servers >= 1000000
? '서버수 제한 해제 (1000000+)'
: bot.servers >= 10000
? '서버수 제한 해제 (10000+)'
: ''
}
+ ${bot.shards >= 200 ? '샤드수 제한 해제' : ''}
`,
'diff'
)
)
return ResponseWrapper(res, { code: 200 })
})
interface PostApiRequest extends NextApiRequest {
query: {
id: string
}
body: ExceedLimit
}
export default BotLimit

View File

@ -17,7 +17,7 @@ import {
parseCookie,
redirectTo,
} from '@utils/Tools'
import { ManageBot, getManageBotSchema } from '@utils/Yup'
import { ExceedLimit, ManageBot, getManageBotSchema } from '@utils/Yup'
import { botCategories, botCategoryDescription, botEnforcements, library } from '@utils/Constants'
import { Bot, Theme, User } from '@types'
import { getToken } from '@utils/Csrf'
@ -49,6 +49,7 @@ const ManageBotPage: NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme }
const [adminModal, setAdminModal] = useState(false)
const [transferModal, setTransferModal] = useState(false)
const [deleteModal, setDeleteModal] = useState(false)
const [limitModal, setLimitModal] = useState(false)
const router = useRouter()
async function submitBot(value: ManageBot) {
@ -136,7 +137,9 @@ const ManageBotPage: NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme }
{data.message || '오류가 발생했습니다.'}
</h2>
<ul className='list-inside list-disc'>
{data.errors?.map((el, n) => <li key={n}>{el}</li>)}
{data.errors?.map((el, n) => (
<li key={n}>{el}</li>
))}
</ul>
</Message>
</div>
@ -664,6 +667,77 @@ const ManageBotPage: NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme }
</Segment>
</div>
)}
{checkUserFlag(user.flags, 'staff') && (
<div className='py-4'>
<Divider />
<h2 className='pb-2 text-2xl font-semibold'> </h2>
<Segment>
<div className='items-center lg:flex'>
<div className='grow py-1'>
<h3 className='text-lg font-semibold'>/ </h3>
<p className='text-gray-400'>10000, 1000000, 200 .</p>
</div>
<Button
onClick={() => setLimitModal(true)}
className='lg:w-1/8 h-10 bg-koreanbots-blue text-white hover:opacity-80'
>
<i className='fas fa-unlock' />
</Button>
<Modal
full
header={`${bot.name} 서버/샤드수 제한 해제`}
isOpen={limitModal}
dark={theme === 'dark'}
onClose={() => setLimitModal(false)}
closeIcon
>
<Formik
initialValues={{
_captcha: '',
_csrf: csrfToken,
servers: bot.servers,
shards: bot.shards ?? 0,
}}
onSubmit={async (value) => {
const res = await Fetch(`/bots/${bot.id}/limit`, {
method: 'PATCH',
body: JSON.stringify(cleanObject<ExceedLimit>(value)),
})
if (res.code === 200) {
alert('성공적으로 수정하였습니다.')
router.push(makeBotURL(bot))
} else alert(res.message)
}}
>
{({ values, setFieldValue }) => (
<Form>
<div className='py-4'>
<h2 className='text-md my-1'> .</h2>
<Input name='servers' placeholder={String(bot.servers)} type='number' />
<h2 className='text-md my-1'> .</h2>
<Input name='shards' placeholder={String(bot.shards)} type='number' />
</div>
<Captcha
dark={theme === 'dark'}
onVerify={(k) => setFieldValue('_captcha', k)}
/>
<Button
disabled={!values._captcha}
className={`mt-4 bg-koreanbots-blue text-white ${
!values._captcha ? 'opacity-80' : 'hover:opacity-80'
}`}
type='submit'
>
<i className='fas fa-save' />
</Button>
</Form>
)}
</Formik>
</Modal>
</div>
</Segment>
</div>
)}
</Container>
)
}

View File

@ -459,4 +459,16 @@ export interface EditBotOwner {
_captcha: string
}
export const ExceedLimitScehma: Yup.SchemaOf<ExceedLimit> = Yup.object({
servers: Yup.number().integer().moreThan(-1, '서버 수는 0보다 커야합니다.').required(),
shards: Yup.number().integer().moreThan(-1, '샤드 수는 0보다 커야합니다.').required(),
_csrf: Yup.string().required(),
_captcha: Yup.string().required(),
})
export interface ExceedLimit {
servers: number
shards: number
}
export default Yup