diff --git a/components/Form/Select.tsx b/components/Form/Select.tsx index 4ac1093..c72102f 100644 --- a/components/Form/Select.tsx +++ b/components/Form/Select.tsx @@ -1,6 +1,6 @@ import ReactSelect from 'react-select' -const Select = ({ placeholder, options, handleChange, handleTouch }: SelectProps): JSX.Element => { +const Select = ({ placeholder, options, handleChange, handleTouch, value }: SelectProps): JSX.Element => { return ( '검색 결과가 없습니다.'} + defaultValue={value} /> ) } @@ -33,6 +34,7 @@ interface SelectProps { handleChange: (value: Option) => void handleTouch: () => void options: Option[] + value?: Option } interface Option { diff --git a/components/Segment.tsx b/components/Segment.tsx index 682465a..b4667a7 100644 --- a/components/Segment.tsx +++ b/components/Segment.tsx @@ -1,3 +1,5 @@ +import { ReactNode } from 'react' + const Segment = ({ children, className = '' }: SegmentProps): JSX.Element => { return (
{ interface SegmentProps { className?: string - children: JSX.Element | JSX.Element[] + children: ReactNode } export default Segment diff --git a/pages/api/v2/bots/[id]/index.ts b/pages/api/v2/bots/[id]/index.ts index 9ba3c11..f4f1350 100644 --- a/pages/api/v2/bots/[id]/index.ts +++ b/pages/api/v2/bots/[id]/index.ts @@ -1,10 +1,12 @@ import { NextApiRequest } from 'next' -import { get, put } from '@utils/Query' +import { get, put, update } from '@utils/Query' import ResponseWrapper from '@utils/ResponseWrapper' import { checkToken } from '@utils/Csrf' -import { AddBotSubmit, AddBotSubmitSchema } from '@utils/Yup' +import { AddBotSubmit, AddBotSubmitSchema, ManageBot, ManageBotSchema } from '@utils/Yup' import RequestHandler from '@utils/RequestHandler' +import { User } from '@types' +import { checkUserFlag } from '@utils/Tools' const Bots = RequestHandler() .get(async (req: GetApiRequest, res) => { @@ -59,8 +61,31 @@ const Bots = RequestHandler() }) return ResponseWrapper(res, { code: 200, data: result }) }) - .patch(async (req, res) => { - return res.send('Reserved') + .patch(async (req: PatchApiRequest, res) => { + const bot = await get.bot.load(req.query.id) + if(!bot) return ResponseWrapper(res, { code: 404, message: '존재하지 않는 봇입니다.' }) + const user = await get.Authorization(req.cookies.token) + if (!user) return ResponseWrapper(res, { code: 401 }) + const userInfo = await get.user.load(user) + if(!(bot.owners as User[]).find(el => el.id === user) && !checkUserFlag(userInfo?.flags, 'staff')) return ResponseWrapper(res, { code: 403 }) + const csrfValidated = checkToken(req, res, req.body._csrf) + if (!csrfValidated) return + + const validated = await ManageBotSchema.validate(req.body, { abortEarly: false }) + .then(el => el) + .catch(e => { + ResponseWrapper(res, { code: 400, errors: e.errors }) + return null + }) + + if (!validated) return + const result = await update.bot(req.query.id, validated) + if(result === 0) return ResponseWrapper(res, { code: 400 }) + else { + get.user.clear(req.query.id) + return ResponseWrapper(res, { code: 200 }) + } + }) interface GetApiRequest extends NextApiRequest { @@ -76,4 +101,11 @@ interface PostApiRequest extends GetApiRequest { } } +interface PatchApiRequest extends GetApiRequest { + body: ManageBot | null + query: { + id: string + } +} + export default Bots diff --git a/pages/manage/[id].tsx b/pages/manage/[id].tsx new file mode 100644 index 0000000..4b86b11 --- /dev/null +++ b/pages/manage/[id].tsx @@ -0,0 +1,174 @@ +import { NextPage, NextPageContext } from 'next' +import { useState } from 'react' +import { useRouter } from 'next/router' +import dynamic from 'next/dynamic' +import { Form, Formik } from 'formik' +import { ParsedUrlQuery } from 'node:querystring' + +import { get } from '@utils/Query' +import { checkUserFlag, cleanObject, makeBotURL, parseCookie, redirectTo } from '@utils/Tools' +import { AddBotSubmit, ManageBot, ManageBotSchema } from '@utils/Yup' +import { categories, library } from '@utils/Constants' +import { Bot, ResponseProps, SubmittedBot, Theme, User } from '@types' +import { getToken } from '@utils/Csrf' +import Fetch from '@utils/Fetch' + +import NotFound from 'pages/404' +import Forbidden from '@components/Forbidden' + +const Label = dynamic(() => import('@components/Form/Label')) +const Input = dynamic(() => import('@components/Form/Input')) +const Divider = dynamic(() => import('@components/Divider')) +const TextArea = dynamic(() => import('@components/Form/TextArea')) +const Segment = dynamic(() => import('@components/Segment')) +const Markdown = dynamic(() => import('@components/Markdown')) +const Select = dynamic(() => import('@components/Form/Select')) +const Selects = dynamic(() => import('@components/Form/Selects')) +const Button = dynamic(() => import('@components/Button')) +const Container = dynamic(() => import('@components/Container')) +const DiscordAvatar = dynamic(() => import('@components/DiscordAvatar')) +const Message = dynamic(() => import('@components/Message')) +const SEO = dynamic(() => import('@components/SEO')) + +const ManageBotPage:NextPage = ({ bot, user, csrfToken, theme }) => { + const [ data, setData ] = useState(null) + const router = useRouter() + function toLogin() { + localStorage.redirectTo = window.location.href + redirectTo(router, 'login') + } + + async function submitBot(value: ManageBot) { + const res = await Fetch(`/bots/${bot.id}`, { method: 'PATCH', body: JSON.stringify(cleanObject(value)) }) + setData(res) + } + if(!bot) return + if(!user) { + toLogin() + return + } + if(!(bot.owners as User[]).find(el => el.id === user?.id) && !checkUserFlag(user?.flags, 'staff')) return + return + +

봇 관리하기

+ + {({ errors, touched, values, setFieldTouched, setFieldValue }) => ( +
+
+ +
+

{bot.name}#{bot.tag}

+

ID: {bot.id}

+
+
+ { + data ? data.code === 200 ?
+ +

정보를 저장했습니다.

+

반영까지는 시간이 조금 걸릴 수 있습니다!

+ {redirectTo(router, makeBotURL(bot))} +
+
:
+ +

{data.message || '오류가 발생했습니다.'}

+
    + {data.errors?.map((el, n) =>
  • {el}
  • )} +
+
+
: '' + } + + + + + { + values.category.includes('빗금 명령어') && +

해당 봇은 빗금 명령어(Slash Command) 카테고리가 선택되었습니다.

+

초대링크는 빗금 명령어 권한을 부여하지 않은 일반 봇 초대링크로 자동 생성됩니다. + 따라서 빗금 명령어 권한을 포함한 초대링크를 직접 설정해주세요.

+
+ } + + + +