diff --git a/.env.demo.local b/.env.demo.local index ea834e5..9332a01 100644 --- a/.env.demo.local +++ b/.env.demo.local @@ -4,6 +4,11 @@ MYSQL_USER=root MYSQL_PASSWORD=YOUSHALLNOTPASS MYSQL_DATABASE=discordbots +MONGO_HOST=mongo +MONGO_USER=discordbots +MONGO_PASSWORD=YOUSHALLNOTPASS +MONGO_DATABASE=discordbots + DISCORD_CLIENT_ID=CLIENT_ID DISCORD_CLIENT_SECRET=CLIENT_SECRET DISCORD_SCOPE=SCOPE diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..9be1b55 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,43 @@ +name: Publish +on: + push: + branches: [master] + pull_request: # debug + branches: '*' + tags: '*' + +jobs: + image-push: + name: Push docker image + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: docker/setup-buildx-action@v1 + - name: Parse image tag + run: | + parsed=${GITHUB_REF#refs/*/} + echo "RELEASE_TAG=${parsed//\//-}" >> $GITHUB_ENV + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + - name: Build and push + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + NEXT_PUBLIC_SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + IMAGE_TAG: ${{ github.sha }} + run: | + printf 'defaults.url=https://sentry.io/\ndefaults.org=koreanbots\ndefaults.project=client' > sentry.properties + docker build --build-arg SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN --build-arg NEXT_PUBLIC_SENTRY_DSN=$NEXT_PUBLIC_SENTRY_DSN --build-arg SENTRY_DSN=$SENTRY_DSN --build-arg SOURCE_COMMIT=${{ env.GITHUB_SHA }} --build-arg TAG=${{ env.RELEASE_TAG }} -t koreanlist . + docker tag koreanlist:latest ${{ secrets.AWS_IMAGE_URL }}:latest + docker tag koreanlist:latest ${{ secrets.AWS_IMAGE_URL }}:${{ env.RELEASE_TAG == 'master' && 'nightly' || env.RELEASE_TAG }} + docker push ${{ secrets.AWS_IMAGE_URL }} --all-tags \ No newline at end of file diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 3051e4b..3a90fd3 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -24,7 +24,7 @@ jobs: env: CI: true test: - name: Run Test + name: Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -34,56 +34,25 @@ jobs: node-version: 14 - name: yarn install run: yarn install - - name: Setup MySQL - uses: getong/mariadb-action@v1.1 - with: - mysql database: 'discordbots' - mysql root password: 'test' - name: Run Jest run: yarn test - - name: Generate RSA Key Pair - run: | - ssh-keygen -b 2048 -t rsa -f key -q -P "" - ssh-keygen -b 2048 -e -m pem -f key -q -P "" > private.key - mv key public.pem - rm key.pub - - name: Setup environments - run: | - mv .env.demo.local .env.production.local - printf 'MARIADB_ROOT_PASSWORD=YOUSHALLNOTPASS\nCOMMIT_HASH=${{ github.sha }}' > .env - printf 'defaults.url=https://sentry.io/\ndefaults.org=koreanbots\ndefaults.project=client' > sentry.properties + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: install node v14 + uses: actions/setup-node@v2 + with: + node-version: 14 + - name: yarn install + run: yarn install - name: Build - run: yarn build + run: | + printf 'defaults.url=https://sentry.io/\ndefaults.org=koreanbots\ndefaults.project=client' > sentry.properties + yarn build env: CI: true SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} NEXT_PUBLIC_SENTRY_DSN: ${{ secrets.SENTRY_DSN }} SENTRY_DSN: ${{ secrets.SENTRY_DSN }} - - # docker: - # needs: - # - eslint - # - build - # - test - # name: Docker Image CI - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v2 - # - name: install node v14 - # uses: actions/setup-node@v1 - # with: - # node-version: 14 - # - name: Generate RSA Key Pair - # run: | - # ssh-keygen -b 2048 -t rsa -f key -q -P "" - # ssh-keygen -b 2048 -e -m pem -f key -q -P "" > private.key - # mv key public.pem - # rm key.pub - # - name: Setup environments - # run: | - # mv .env.demo.local .env.production.local - # printf 'MARIADB_ROOT_PASSWORD=YOUSHALLNOTPASS\nCOMMIT_HASH=${{ github.sha }}' > .env - # - name: Create needed files - # run: echo '{"tester":"DEMO_KEY"}' > secret.json - # - name: Docker Compose - # run: docker-compose up -d diff --git a/components/Application.tsx b/components/Application.tsx index 9409bae..75d2b26 100644 --- a/components/Application.tsx +++ b/components/Application.tsx @@ -2,11 +2,16 @@ import dynamic from 'next/dynamic' import Link from 'next/link' const DiscordAvatar = dynamic(() => import('@components/DiscordAvatar')) +const ServerIcon = dynamic(() => import('@components/ServerIcon')) const Application: React.FC = ({ type, id, name }) => { return
- + { + type === 'bot' ? + : + + }

{name}

@@ -14,7 +19,7 @@ const Application: React.FC = ({ type, id, name }) => { } interface ApplicationProps { - type: 'bot' + type: 'bot' | 'server' id: string name: string } diff --git a/components/BotCard.tsx b/components/BotCard.tsx index 4e0e95b..bb9fc6e 100644 --- a/components/BotCard.tsx +++ b/components/BotCard.tsx @@ -68,7 +68,7 @@ const BotCard: React.FC = ({ manage = false, bot }) => {
{bot.category.slice(0, 3).map(el => ( - + ))}{' '} {bot.category.length > 3 && }
@@ -96,8 +96,7 @@ const BotCard: React.FC = ({ manage = false, bot }) => { : = ({ children, enabled, docs, c const [ navbarEnabled, setNavbarOpen ] = useState(false) return
-
@@ -43,7 +43,7 @@ const DeveloperLayout: React.FC = ({ children, enabled, docs, c
  • - 나의 봇 + 나의 리스트
  • diff --git a/components/DiscordAvatar.tsx b/components/DiscordAvatar.tsx index d248950..1238914 100644 --- a/components/DiscordAvatar.tsx +++ b/components/DiscordAvatar.tsx @@ -1,44 +1,16 @@ -import { SyntheticEvent, useEffect, useState } from 'react' +import { SyntheticEvent } from 'react' +import dynamic from 'next/dynamic' import { KoreanbotsEndPoints } from '@utils/Constants' -import { supportsWebP } from '@utils/Tools' -import Logger from '@utils/Logger' + +const Image = dynamic(() => import('@components/Image')) const DiscordAvatar: React.FC = props => { - const fallback = '/img/default.png' - const [ webpUnavailable, setWebpUnavailable ] = useState() - - useEffect(()=> { - setWebpUnavailable(localStorage.webp === 'false') - }, []) - - return {props.alt)=> { - if(webpUnavailable) { - (e.target as ImageTarget).onerror = (event) => { - // All Fails - (event.target as ImageTarget).onerror = ()=> { Logger.warn('FALLBACK IMAGE LOAD FAIL') } - (event.target as ImageTarget).src = fallback - - } - } - else { - (e.target as ImageTarget).onerror = (event) => { - // All Fails - (event.target as ImageTarget).onerror = ()=> { Logger.warn('FALLBACK IMAGE LOAD FAIL') } - (event.target as ImageTarget).src = fallback - } - // Webp Load Fail - (e.target as ImageTarget).src = KoreanbotsEndPoints.CDN.avatar(props.userID, { size: props.size ?? 256}) - if(!supportsWebP()) localStorage.setItem('webp', 'false') - } - }} + return + } interface DiscordAvatarProps { diff --git a/components/Footer.tsx b/components/Footer.tsx index fe01537..a0cfcc5 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -12,8 +12,8 @@ const Footer: React.FC = ({ theme, setTheme }) => {
    -

    국내 디스코드 봇을 한 곳에서.

    - 2020-2021 Koreanbots, All rights reserved. +

    국내 디스코드의 모든 것을 한 곳에서.

    + 2020-2021 한국 디스코드 리스트, All rights reserved.
    -

    한국 디스코드봇 리스트

    +

    한국 디스코드 리스트

    @@ -154,6 +184,22 @@ const Navbar: React.FC = ({ token }) => { + { + type !== 'bot' && + setNavbarOpen(false)} className='flex items-center px-8 py-2 text-gray-100 hover:text-gray-300'> + + 봇 리스트 + + + } + { + type !== 'server' && + setNavbarOpen(false)} className='flex items-center px-8 py-2 text-gray-100 hover:text-gray-300'> + + 서버 리스트 + + + } setNavbarOpen(false)} className='flex items-center px-8 py-2 text-gray-100 hover:text-gray-300'> @@ -166,12 +212,22 @@ const Navbar: React.FC = ({ token }) => { 소개 - - setNavbarOpen(false)} className='flex items-center px-8 py-2 text-gray-100 hover:text-gray-300'> - - 봇 추가하기 - - + { + type === 'bot' && + setNavbarOpen(false)} className='flex items-center px-8 py-2 text-gray-100 hover:text-gray-300'> + + 봇 추가하기 + + + } + { + type === 'server' && + setNavbarOpen(false)} className='flex items-center px-8 py-2 text-gray-100 hover:text-gray-300'> + + 서버 추가하기 + + + }
    diff --git a/components/Owner.tsx b/components/Owner.tsx index 6d76968..dbdcf21 100644 --- a/components/Owner.tsx +++ b/components/Owner.tsx @@ -1,7 +1,7 @@ import Link from 'next/link' import DiscordAvatar from '@components/DiscordAvatar' -const Owner: React.FC = ({ id, username, tag }) => { +const Owner: React.FC = ({ id, username, tag, crown=false }) => { return ( @@ -9,8 +9,9 @@ const Owner: React.FC = ({ id, username, tag }) => {
    -

    {username}

    +

    { crown && }{username}

    #{tag} +
    @@ -23,4 +24,5 @@ interface OwnerProps { id: string tag: string username: string + crown?: boolean } diff --git a/components/SEO.tsx b/components/SEO.tsx index e040612..d29c9fc 100644 --- a/components/SEO.tsx +++ b/components/SEO.tsx @@ -3,7 +3,7 @@ import Head from 'next/head' const SEO: React.FC = ({ title, description, image }: SEOProps) => { return ( - {title} - 한국 디스코드봇 리스트 + {title} - 한국 디스코드 리스트 {description && } {description && } diff --git a/components/Search.tsx b/components/Search.tsx index f1d6f95..6580d86 100644 --- a/components/Search.tsx +++ b/components/Search.tsx @@ -1,22 +1,25 @@ import { useEffect, useRef, useState } from 'react' import { useRouter } from 'next/router' import Link from 'next/link' +import dynamic from 'next/dynamic' import AbortController from 'abort-controller' -import { makeBotURL, redirectTo } from '@utils/Tools' +import { makeBotURL, makeServerURL, redirectTo } from '@utils/Tools' import Fetch from '@utils/Fetch' -import { BotList, ResponseProps } from '@types' +import { Bot, Server, ResponseProps } from '@types' -import DiscordAvatar from '@components/DiscordAvatar' import Day from '@utils/Day' import useOutsideClick from '@utils/useOutsideClick' +const DiscordAvatar = dynamic(() => import('@components/DiscordAvatar')) +const ServerIcon = dynamic(() => import('@components/ServerIcon')) + const Search: React.FC = () => { const router = useRouter() const ref = useRef() const [query, setQuery] = useState('') const [recentSearch, setRecentSearch] = useState([]) - const [data, setData] = useState>(null) + const [data, setData] = useState>(null) const [loading, setLoading] = useState(false) const [abortControl, setAbortControl] = useState(new AbortController()) const [hidden, setHidden] = useState(true) @@ -32,6 +35,7 @@ const Search: React.FC = () => { }, [router]) useOutsideClick(ref, () => setHidden(true)) const SearchResults = async (value: string) => { + setData(null) setQuery(value) try { abortControl.abort() @@ -41,7 +45,7 @@ const Search: React.FC = () => { const controller = new AbortController() setAbortControl(controller) if (value.length > 1) setLoading(true) - const res = await Fetch(`/search/bots?q=${encodeURIComponent(value)}`, { + const res = await Fetch(`/search/all?q=${encodeURIComponent(value)}`, { signal: controller.signal, }).catch((e) => { if(e.name !== 'AbortError') throw e @@ -105,72 +109,94 @@ const Search: React.FC = () => {
      - {data && data.code === 200 && data.data ? ( - data.data.data.length === 0 ? ( -
    • 검색 결과가 없습니다.
    • - ) : ( - data.data.data.map(el => ( - -
    • - -
      -

      {el.name}

      -

      {el.intro}

      -
      -
    • - - )) - ) - ) : loading ? ( -
    • 검색중입니다...
    • - ) : ( - <> - {query && data ? ( - data.message?.includes('문법') ? ( -
    • - 검색 문법이 잘못되었습니다. -
      - - 더 알아보기 - -
    • - ) :
    • {(data.errors && data.errors[0]) || data.message}
    • - ) : query.length === 0 ? !recentSearch || !Array.isArray(recentSearch) || recentSearch.length === 0?
    • 최근 검색 기록이 없습니다.
    • - : <> -
    • - 최근 검색어 - -
    • - { - recentSearch.slice(0, 10).map((el, n) => ( - -
    • - {el?.value} - - {Day(el?.date).format('MM.DD.')} - + {(data && data.code === 200) ? ( +
      +
        +
      • + { + data.data.bots.length === 0 ? +
      • 검색 결과가 없습니다.
      • : + data.data.bots.map(el => ( + +
      • + +
        +

        {el.name}

        +

        {el.intro}

        +
      • )) - } - : - query.length < 3 ? ( - '최소 2글자 이상 입력해주세요.' - ) : ( - '검색어를 입력해주세요.' - )} - - )} + } +
      +
        +
      • 서버
      • + { + data.data.servers.length === 0 ? +
      • 검색 결과가 없습니다.
      • : + data.data.servers.map(el => ( + +
      • + +
        +

        {el.name}

        +

        {el.intro}

        +
        +
      • + + )) + } +
      +
      + ) : loading ?
        +
      • 검색중입니다...
      • +
      :
        + {query && data ? ( + data.message?.includes('문법') ? ( +
      • + 검색 문법이 잘못되었습니다. +
        + + 더 알아보기 + +
      • + ) :
      • {(data.errors && data.errors[0]) || data.message || '검색중입니다...'}
      • + ) : query.length === 0 ? !recentSearch || !Array.isArray(recentSearch) || recentSearch.length === 0?
      • 최근 검색 기록이 없습니다.
      • + : <> +
      • + 최근 검색어 + +
      • + { + recentSearch.slice(0, 10).map((el, n) => ( + +
      • + {el?.value} + + {Day(el?.date).format('MM.DD.')} + +
      • + + )) + } + : + query.length < 3 ? ( + '최소 2글자 이상 입력해주세요.' + ) : ( + '검색어를 입력해주세요.' + )} +
      + }
    @@ -179,3 +205,8 @@ const Search: React.FC = () => { } export default Search + +interface ListAll { + bots: Bot[], + servers: Server[] +} \ No newline at end of file diff --git a/components/ServerCard.tsx b/components/ServerCard.tsx new file mode 100644 index 0000000..e129949 --- /dev/null +++ b/components/ServerCard.tsx @@ -0,0 +1,164 @@ +import Link from 'next/link' +import dynamic from 'next/dynamic' + +import { checkServerFlag, formatNumber, makeServerURL } from '@utils/Tools' +import { ServerData, ServerState } from '@types' +import { DiscordEnpoints, DSKR_BOT_ID } from '@utils/Constants' + +const Divider = dynamic(() => import('@components/Divider')) +const Tag = dynamic(() => import('@components/Tag')) +const ServerIcon = dynamic(() => import('@components/ServerIcon')) + +const ServerCard: React.FC = ({ type, server }) => { + const newServerLink = server.data ? `/addserver/${server.id}` : `${DiscordEnpoints.InviteApplication(DSKR_BOT_ID, {}, 'bot', null, server.id)}&disable_guild_select=true` + return
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    + +
    +

    + 정보 갱신 불가 +

    +

    {server.name}

    +
    +
    +
    + + {formatNumber(server.votes)} + + } + dark + /> + {formatNumber(server.members)} 멤버 : 'N/A'} + dark + /> +
    +
    + +

    + {type === 'add' ? + server.data ? '지금 바로 서버를 등록할 수 있습니다.' : '봇을 초대해야 서버를 등록할 수 있습니다.' + : server.intro + } +

    +
    +
    + {server.category?.slice(0, 3).map(el => ( + + ))}{' '} + {server.category?.length > 3 && } +
    +
    +
    + + +
    +
    + { + type === 'add' ? + server.data ? + + 등록하기 + + : + + 봇 초대하기 + + + : + <> + + + 보기 + + + {type === 'manage' ? ( + + + 관리하기 + + + ) : !['ok', 'unreachable'].includes(server.state) ? + 참가하기 + : + + 참가하기 + + } + + + } +
    +
    +
    +
    +
    +
    +
    + +} + +interface BotCardProps { + type: 'list' | 'manage' | 'add' + server: { + id: string, + name: string, + intro?: string + desc?: string, + flags?: number + state?: ServerState + icon: string | null, + banner?: string | null, + bg?: string | null, + vanity?: string | null + category?: string[] + votes?: number | null + members?: number | null, + data?: ServerData + + } +} + +export default ServerCard \ No newline at end of file diff --git a/components/ServerIcon.tsx b/components/ServerIcon.tsx new file mode 100644 index 0000000..ca23b20 --- /dev/null +++ b/components/ServerIcon.tsx @@ -0,0 +1,35 @@ +import { SyntheticEvent } from 'react' +import dynamic from 'next/dynamic' +import { DiscordEnpoints, KoreanbotsEndPoints } from '@utils/Constants' + +const Image = dynamic(() => import('@components/Image')) + +const ServerIcon: React.FC = ({ id, size, className, alt, hash }) => { + return {alt} + +} + +interface ServerIconProps { + alt?: string + id: string + hash?: string + fromDiscord?: boolean + className?: string + size? : 128 | 256 | 512 +} + +interface ImageEvent extends Event { + target: ImageTarget +} + +interface ImageTarget extends EventTarget { + src: string + onerror: (event: SyntheticEvent) => void +} + +export default ServerIcon diff --git a/docker-compose-stable.yml b/docker-compose-stable.yml index f3add94..14ad510 100644 --- a/docker-compose-stable.yml +++ b/docker-compose-stable.yml @@ -2,9 +2,10 @@ version: '3' services: mysql: + container_name: mysql-stable + restart: always image: wonderlandpark/mariadb-mroonga:latest hostname: mysql - container_name: mysql-stable env_file: - .env command: @@ -12,12 +13,26 @@ services: - --collation-server=utf8mb4_unicode_ci volumes: - /home/ubuntu/mysql:/var/lib/mysql + mongo: + container_name: mongo-stable + restart: always + image: mongo:5.0 + hostname: mongo + env_file: + - .env + volumes: + - /home/ubuntu/mongo:/data/db web: container_name: web-stable + restart: always + image: wonderlandpark/koreanbots:nightly ports: - - 5000:3000 + - 3000:3000 links: - mysql env_file: - .env.production.local - image: wonderlandpark/koreanbots:stable + deploy: + resources: + limits: + memory: 500M diff --git a/docker-compose.yml b/docker-compose.yml index 0cc9c70..2956677 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,9 +2,10 @@ version: '3' services: mysql: + container_name: mysql + restart: always image: wonderlandpark/mariadb-mroonga:latest hostname: mysql - container_name: mysql env_file: - .env command: @@ -12,15 +13,34 @@ services: - --collation-server=utf8mb4_unicode_ci volumes: - /home/ubuntu/mysql-beta:/var/lib/mysql + mongo: + container_name: mongo + restart: always + image: mongo:5.0 + hostname: mongo + env_file: + - .env + volumes: + - /home/ubuntu/mongo-beta:/data/db + bot: + container_name: bot + restart: always + image: 397554924689.dkr.ecr.ap-northeast-2.amazonaws.com/dskr + links: + - mongo + env_file: + - .env web: container_name: web + restart: always + image: 397554924689.dkr.ecr.ap-northeast-2.amazonaws.com/koreanlist ports: - 4000:3000 links: - mysql + - mongo env_file: - .env.beta.production.local - image: wonderlandpark/koreanbots:nightly deploy: resources: limits: diff --git a/next-env.d.ts b/next-env.d.ts index c6643fd..9bc3dd4 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,3 +1,6 @@ /// /// /// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/package.json b/package.json index 54eaa01..fda2ea3 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "josa": "3.0.1", "jsonwebtoken": "8.5.1", "knex": "^0.95.8", + "mongoose": "^5.13.4", "mysql": "2.18.1", "next": "^11.1.0", "next-connect": "0.10.1", diff --git a/pages/_app.tsx b/pages/_app.tsx index 71f463d..02fc407 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -59,7 +59,7 @@ const KoreanbotsApp = ({ Component, pageProps, err, cookie }: KoreanbotsProps): return
    import('@components/Container')) const About:NextPage = () => { return
    - “국내 디스코드 봇을 한 곳에서.”} subheader='한국 디스코드봇 리스트에서 자신의 서버에 딱 맞는 봇을 찾아보세요.'> + “국내 디스코드의 모든 것을 한 곳에서.”} subheader='한국 디스코드 리스트에서 자신에게 필요한 디스코드의 모든 것을 찾아보세요!'>

    소개

    -

    한국 디스코드봇 리스트는 본인의 봇을 직접 등록하고, 봇이 필요한 유저는 필요한 봇을 카테고리별로 확인할 수 있는 플랫폼입니다.

    -

    자신의 봇을 등록하거나 필요한 봇을 찾아보세요!

    +

    한국 디스코드 리스트는 본인의 봇과 서버를 직접 등록하고, 유저 분은 봇 또는 서버를 카테고리별로 확인할 수 있는 플랫폼입니다.

    +

    자신에게 필요한 디스코드의 모든 것을 찾아보세요!

    특징

    하트 시스템

    -

    유용한 봇에 투표하는 하트 시스템으로 여러 유용한 봇이 상단에 노출될 수 있는 기회를 제공합니다.

    +

    마음에 드는 봇이나 서버에 투표하는 하트 시스템으로 유용한 봇 또는 서버가 상단에 노출될 수 있는 기회를 제공합니다.

    인증 시스템

    -

    디스코드 봇 인증보다 한 단계 까다로운 기준을 적용하여, 이용자분들에게 신뢰감을 줍니다.

    +

    봇은 디스코드 봇 인증보다 한 단계 까다로운 기준을 적용하며 서버는 신뢰할 수 있는 서버를 정해, 이용자분들에게 신뢰감을 줍니다.

    API 제공

    -

    봇 정보부터, 유저 투표 여부 확인, 봇의 svg 라벨까지.
    다양한 API를 제공하여 커스텀할 수 있습니다!

    +

    정보부터, 유저 투표 여부 확인, 위젯까지.
    다양한 API를 제공하여 커스텀할 수 있습니다!

    @@ -39,7 +39,7 @@ const About:NextPage = () => {

    - 국내 디스코드봇을 한 곳에서. + 국내 디스코드의 모든 것을 한 곳에서.

    diff --git a/pages/addbot.tsx b/pages/addbot.tsx index 84cfec2..3ad0d04 100644 --- a/pages/addbot.tsx +++ b/pages/addbot.tsx @@ -10,7 +10,7 @@ import HCaptcha from '@hcaptcha/react-hcaptcha' import { get } from '@utils/Query' import { cleanObject, parseCookie, redirectTo } from '@utils/Tools' import { AddBotSubmit, AddBotSubmitSchema } from '@utils/Yup' -import { categories, library } from '@utils/Constants' +import { botCategories, library } from '@utils/Constants' import { getToken } from '@utils/Csrf' import Fetch from '@utils/Fetch' import { ResponseProps, SubmittedBot, Theme, User } from '@types' @@ -71,13 +71,13 @@ const AddBot:NextPage = ({ logged, user, csrfToken, theme }) => { } if(!logged) return - return -

    새로운 봇 추가하기

    @@ -136,7 +136,7 @@ const AddBot:NextPage = ({ logged, user, csrfToken, theme }) => { +
    + + + +