diff --git a/pages/api/v2/bots/[id]/index.ts b/pages/api/v2/bots/[id]/index.ts index cdfdfcd..45f4d6c 100644 --- a/pages/api/v2/bots/[id]/index.ts +++ b/pages/api/v2/bots/[id]/index.ts @@ -10,8 +10,8 @@ import { AddBotSubmit, AddBotSubmitSchema, CsrfCaptcha, + getManageBotSchema, ManageBot, - ManageBotSchema, } from '@utils/Yup' import RequestHandler from '@utils/RequestHandler' import { User } from '@types' @@ -197,6 +197,10 @@ const Bots = RequestHandler() if (!bot) return ResponseWrapper(res, { code: 404, message: '존재하지 않는 봇입니다.' }) const user = await get.Authorization(req.cookies.token) if (!user) return ResponseWrapper(res, { code: 401 }) + + const isPerkAvailable = + checkBotFlag(bot.flags, 'partnered') || checkBotFlag(bot.flags, 'trusted') + const userInfo = await get.user.load(user) if ( ['reported', 'blocked', 'archived'].includes(bot.state) && @@ -215,7 +219,7 @@ const Bots = RequestHandler() const csrfValidated = checkToken(req, res, req.body._csrf) if (!csrfValidated) return - const validated: ManageBot = await ManageBotSchema.validate(req.body, { abortEarly: false }) + const validated: ManageBot = await getManageBotSchema(isPerkAvailable).validate(req.body, { abortEarly: false }) .then((el) => el) .catch((e) => { ResponseWrapper(res, { code: 400, errors: e.errors }) @@ -223,11 +227,7 @@ const Bots = RequestHandler() }) if (!validated) return - if ( - !checkBotFlag(bot.flags, 'trusted') && - !checkBotFlag(bot.flags, 'partnered') && - (validated.vanity || validated.banner || validated.bg) - ) + if (!isPerkAvailable && (validated.vanity || validated.banner || validated.bg)) return ResponseWrapper(res, { code: 403, message: '해당 봇은 특전을 이용할 권한이 없습니다.', @@ -260,7 +260,7 @@ const Bots = RequestHandler() }, ], color: Colors.Blue, - } + }, ], }) } diff --git a/pages/bots/[id]/edit.tsx b/pages/bots/[id]/edit.tsx index 913aee6..55bbc2e 100644 --- a/pages/bots/[id]/edit.tsx +++ b/pages/bots/[id]/edit.tsx @@ -10,7 +10,7 @@ import { getJosaPicker } from 'josa' import { get } from '@utils/Query' import { checkBotFlag, checkUserFlag, cleanObject, makeBotURL, parseCookie, redirectTo } from '@utils/Tools' -import { ManageBot, ManageBotSchema } from '@utils/Yup' +import { ManageBot, getManageBotSchema } from '@utils/Yup' import { botCategories, botCategoryDescription, library } from '@utils/Constants' import { Bot, Theme, User } from '@types' import { getToken } from '@utils/Csrf' @@ -93,7 +93,7 @@ const ManageBotPage: NextPage = ({ bot, user, csrfToken, theme } bg: isPerkAvailable && bot.bg, _csrf: csrfToken, })} - validationSchema={ManageBotSchema} + validationSchema={getManageBotSchema(isPerkAvailable)} onSubmit={submitBot} > {({ errors, touched, values, setFieldTouched, setFieldValue }) => ( diff --git a/utils/Yup.ts b/utils/Yup.ts index 37c41ef..91d984c 100644 --- a/utils/Yup.ts +++ b/utils/Yup.ts @@ -263,70 +263,79 @@ export interface Report { _csrf: string } -export const ManageBotSchema: Yup.SchemaOf = Yup.object({ - prefix: Yup.string() - .matches(Prefix, '접두사는 띄어쓰기로 시작할 수 없습니다.') - .min(1, '접두사는 최소 1자여야합니다.') - .max(32, '접두사는 최대 32자까지만 가능합니다.') - .required('접두사는 필수 항목입니다.'), - library: Yup.string().oneOf(library).required('라이브러리는 필수 항목입니다.'), - website: Yup.string() - .matches(HTTPProtocol, 'http:// 또는 https:// 로 시작해야합니다.') - .matches(Url, '올바른 웹사이트 URL을 입력해주세요.') - .max(64, 'URL은 최대 64자까지만 가능합니다.') - .nullable(), - url: Yup.string() - .matches(HTTPProtocol, 'http:// 또는 https:// 로 시작해야합니다.') - .matches(Url, '올바른 초대링크 URL을 입력해주세요.') - .max(612, 'URL은 최대 612자까지만 가능합니다.') - .nullable(), - git: Yup.string() - .matches(HTTPProtocol, 'http:// 또는 https:// 로 시작해야합니다.') - .matches(Url, '올바른 깃 URL을 입력해주세요.') - .max(64, 'URL은 최대 64자까지만 가능합니다.') - .nullable(), - discord: Yup.string() - .matches(Vanity, '디스코드 초대코드 형식을 지켜주세요.') - .min(2, '지원 디스코드는 최소 2자여야합니다.') - .max(32, '지원 디스코드는 최대 32자까지만 가능합니다.') - .nullable(), - category: Yup.array(Yup.string().oneOf(botCategories)) - .min(1, '최소 한 개의 카테고리를 선택해주세요.') - .unique('카테고리는 중복될 수 없습니다.') - .required('카테고리는 필수 항목입니다.'), - intro: Yup.string() - .min(2, '봇 소개는 최소 2자여야합니다.') - .max(60, '봇 소개는 최대 60자여야합니다.') - .required('봇 소개는 필수 항목입니다.'), - desc: Yup.string() - .min(100, '봇 설명은 최소 100자여야합니다.') - .max(1500, '봇 설명은 최대 1500자여야합니다.') - .required('봇 설명은 필수 항목입니다.'), - vanity: Yup.string() - .matches(Vanity, '커스텀 URL은 영문만 포함할 수 있습니다.') - .when('id', { - is: (id: string) => reservedVanityBypass.includes(id), - then: Yup.string(), - otherwise: Yup.string().matches( - reservedVanity, - '예약어가 포함되었거나 사용할 수 없는 커스텀 URL입니다.' - ), - }) - .min(2, '커스텀 URL은 최소 2자여야합니다.') - .max(32, '커스텀 URL은 최대 32자여야합니다.') - .nullable(), - banner: Yup.string() - .matches(HTTPProtocol, 'http:// 또는 https:// 로 시작해야합니다.') - .matches(Url, '올바른 배너 URL을 입력해주세요.') - .max(612, 'URL은 최대 612자까지만 가능합니다.') - .nullable(), - bg: Yup.string() - .matches(HTTPProtocol, 'http:// 또는 https:// 로 시작해야합니다.') - .matches(Url, '올바른 배경 URL을 입력해주세요.') - .max(612, 'URL은 최대 612자까지만 가능합니다.') - .nullable(), - _csrf: Yup.string().required(), -}) +export function getManageBotSchema(perkAvailable = false) { + const common = { + prefix: Yup.string() + .matches(Prefix, '접두사는 띄어쓰기로 시작할 수 없습니다.') + .min(1, '접두사는 최소 1자여야합니다.') + .max(32, '접두사는 최대 32자까지만 가능합니다.') + .required('접두사는 필수 항목입니다.'), + library: Yup.string().oneOf(library).required('라이브러리는 필수 항목입니다.'), + website: Yup.string() + .matches(HTTPProtocol, 'http:// 또는 https:// 로 시작해야합니다.') + .matches(Url, '올바른 웹사이트 URL을 입력해주세요.') + .max(64, 'URL은 최대 64자까지만 가능합니다.') + .nullable(), + url: Yup.string() + .matches(HTTPProtocol, 'http:// 또는 https:// 로 시작해야합니다.') + .matches(Url, '올바른 초대링크 URL을 입력해주세요.') + .max(612, 'URL은 최대 612자까지만 가능합니다.') + .nullable(), + git: Yup.string() + .matches(HTTPProtocol, 'http:// 또는 https:// 로 시작해야합니다.') + .matches(Url, '올바른 깃 URL을 입력해주세요.') + .max(64, 'URL은 최대 64자까지만 가능합니다.') + .nullable(), + discord: Yup.string() + .matches(Vanity, '디스코드 초대코드 형식을 지켜주세요.') + .min(2, '지원 디스코드는 최소 2자여야합니다.') + .max(32, '지원 디스코드는 최대 32자까지만 가능합니다.') + .nullable(), + category: Yup.array(Yup.string().oneOf(botCategories)) + .min(1, '최소 한 개의 카테고리를 선택해주세요.') + .unique('카테고리는 중복될 수 없습니다.') + .required('카테고리는 필수 항목입니다.'), + intro: Yup.string() + .min(2, '봇 소개는 최소 2자여야합니다.') + .max(60, '봇 소개는 최대 60자여야합니다.') + .required('봇 소개는 필수 항목입니다.'), + desc: Yup.string() + .min(100, '봇 설명은 최소 100자여야합니다.') + .max(1500, '봇 설명은 최대 1500자여야합니다.') + .required('봇 설명은 필수 항목입니다.'), + _csrf: Yup.string().required(), + } + + const perk = { + vanity: Yup.string() + .matches(Vanity, '커스텀 URL은 영문만 포함할 수 있습니다.') + .when('id', { + is: (id: string) => reservedVanityBypass.includes(id), + then: Yup.string(), + otherwise: Yup.string().matches( + reservedVanity, + '예약어가 포함되었거나 사용할 수 없는 커스텀 URL입니다.' + ), + }) + .min(2, '커스텀 URL은 최소 2자여야합니다.') + .max(32, '커스텀 URL은 최대 32자여야합니다.') + .nullable(), + banner: Yup.string() + .matches(HTTPProtocol, 'http:// 또는 https:// 로 시작해야합니다.') + .matches(Url, '올바른 배너 URL을 입력해주세요.') + .max(612, 'URL은 최대 612자까지만 가능합니다.') + .nullable(), + bg: Yup.string() + .matches(HTTPProtocol, 'http:// 또는 https:// 로 시작해야합니다.') + .matches(Url, '올바른 배경 URL을 입력해주세요.') + .max(612, 'URL은 최대 612자까지만 가능합니다.') + .nullable(), + } + return Yup.object({ + ...common, + ...(perkAvailable ? perk : {}), + }) as Yup.SchemaOf +} export interface ManageBot { prefix: string