From 678fae41125cdfe35172c1d0429c9b119e268e27 Mon Sep 17 00:00:00 2001 From: Junseo Park Date: Sat, 6 Nov 2021 23:57:46 +0900 Subject: [PATCH] Feature/serverlist skeleton (#468) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * deps: added mongoose * feat(*): added mongo and saving invited count * chore(env): updated mongo configuration * chore: updated next-env.d.ts * chore(*): changed categories to botCategories * chore(Image): maded image component * feat(ServerCard): added ServerCard component * feat(ServerIcon): added ServerIcon component * feat(Tools): added server related functions * feat(Mongo): added serverSchema * chore(Hero): support serverlist * feat(Owner): added crown * feat(icons): added icons api * feat(Yup): added AddServerSubmitSchema * types: added server related types * chore(BotCard): changed bot category link * chore(Hero): changed category links * feat(ServerCard): added unreachable state display * feat(Yup): added ManageServerSchema * feat(Query): added server related queries * feat(Constants): added server related stuffs * types: added updatedAt field for ServerData * feat(pages/servers/*): added server pages * feat(*): moved bot category rotue * typo: fixed typo issue * feat(pages/addserver/*): added add server page * feat(api/servers): added server related api * feat(pages/servers): added server edit page * feat(pages/bots): changed bot list route * feat(*): server categories * feat(pages/users): added owned server list * chore(pages/bots): changed image size * feat(docker-compose): added bot * ci: made some changes * types: fixed type * types(Search): fixed type * types(*): fixed type * fix(*): missing fields * fix: Hero type typo issue * ci(*): missing sentry org slug * ci(*): fix * feat(*): added and changed search pages * Update pages/addserver/[id].tsx Co-authored-by: Ryu JuHeon * feat(api/search): added servers search api * feat(pages/panel): added server list in manage page * feat(Search): supporting server search at SearchBox * feat(pages/apllications/servers): added server application page * chore(docker-compose): changed image link * chore(utils): removing server cache at submit * chore(image/icons): added debug code * chore(*): changed component names * chore(Query): decreased server cache ttl * fix(Query): error on addserver page close: https://github.com/koreanbots/serverlist-testing/issues/10 * fix(Query): not using vote type close: https://github.com/koreanbots/serverlist-testing/issues/9 * fix(Constants): fixed category unexpected char close: https://github.com/koreanbots/serverlist-testing/issues/8 * fix(Query): serialize server data * fix(Query): returning null on boost level 0 * fix(page/servers): displaying n/a on boostTier null close: https://github.com/koreanbots/serverlist-testing/issues/4 * fix(pages/servers): hiding emoji list if no emoji close: https://github.com/koreanbots/serverlist-testing/issues/1 * typo(pages/servers): bot to server close: https://github.com/koreanbots/serverlist-testing/issues/2 * fix(components/Hero): editing vote list link close: https://github.com/koreanbots/serverlist-testing/issues/11 * chore(*): changed list route * feat(pages/servers/list/votes): added server vote list page close: https://github.com/koreanbots/serverlist-testing/issues/12 * feat(Dockerfile): added pre-build * fix(Image): image broken when fallbackSrc not given close: https://github.com/koreanbots/serverlist-testing/issues/5 * ci: checking out submodules * fix(ServerCard): bot category displayed at ServerCard close: https://github.com/koreanbots/serverlist-testing/issues/16 * feat(*): supporting opengraph image for server * fix(utils/Constants): fixed type missing on og * feat(pages/servers): not forcing emoji width * chore(utils/Yup): fixed agree checkbox error message * typo(utils/Yup): fixed bot to server * feat(pages/servers): improved emoji display * chore(api/images/discord/icons): removed debug code * chore(pages/servers): removed crown for owner close: https://github.com/koreanbots/serverlist-testing/issues/19 * fix(utils/Query): returning date as string close: https://github.com/koreanbots/serverlist-testing/issues/23 * fix(ServerCard): changed manage link from bot manage link * fix(ServerCard): same height for every card * chore: removed debug code * chore(pages/addserver): showing as invite for server kicked bot * typo(*): fixed typo issues * types: added nullable type * feat(Navbar): added list menu * chore: showing warning for server data not fetched * chore: changed main page (combined bots and servers) * typo(*): replace '한국 디스코드봇 리스트' with '한국 디스코드 리스트' * chore: added Hero component combined state * typo: changed name * fix(Navbar): fix link href * typo: fix about page for serverlist * chore: decrease font size * fix: server category tag link * fix: bot category link * feat: added server widget * fix(ServerCard): fixed servername overflowing * chore: forcing re-login when discord server data fetch fails * fix: error causing on owner not registered * fix: making state same for join button * fix: filtering owner if null * fix(servers/[id]): fix error causing if owner is null * fix(addserver): fixed error occuring for users not logged in * fix(Constant): fixed og image extension getting popped * typo: fixed typo issue * fix: showing forbidden page for non-owner users * feat: invite guide for server which bot left * fix: invalid path for paginator on bot page Co-authored-by: Hajin Lim Co-authored-by: Ryu JuHeon --- .env.demo.local | 5 + .github/workflows/publish.yml | 43 ++ .github/workflows/testing.yml | 61 +-- components/Application.tsx | 9 +- components/BotCard.tsx | 5 +- components/DeveloperLayout.tsx | 6 +- components/DiscordAvatar.tsx | 46 +- components/Footer.tsx | 6 +- components/Hero.tsx | 52 ++- components/Image.tsx | 62 +++ components/Navbar.tsx | 82 +++- components/Owner.tsx | 6 +- components/SEO.tsx | 2 +- components/Search.tsx | 167 +++++--- components/ServerCard.tsx | 164 +++++++ components/ServerIcon.tsx | 35 ++ docker-compose-stable.yml | 21 +- docker-compose.yml | 24 +- next-env.d.ts | 3 + package.json | 1 + pages/_app.tsx | 2 +- pages/about.tsx | 14 +- pages/addbot.tsx | 12 +- pages/addserver/[id].tsx | 216 ++++++++++ pages/addserver/index.tsx | 56 +++ pages/api/image/discord/icons/[id].ts | 64 +++ pages/api/v2/applications/bots/[id]/reset.ts | 6 +- .../api/v2/applications/servers/[id]/reset.ts | 38 ++ pages/api/v2/list/bots/new.ts | 4 +- pages/api/v2/list/bots/votes.ts | 4 +- pages/api/v2/search/all.ts | 40 ++ pages/api/v2/search/bots.ts | 6 +- pages/api/v2/search/servers.ts | 39 ++ pages/api/v2/servers/[id]/index.ts | 175 ++++++++ pages/api/v2/servers/[id]/owners.ts | 20 + pages/api/v2/servers/[id]/report.ts | 53 +++ pages/api/v2/servers/[id]/vote.ts | 57 +++ pages/api/widget/servers/[type]/[id].ts | 65 +++ pages/bots/[id]/edit.tsx | 4 +- pages/bots/[id]/index.tsx | 15 +- pages/bots/[id]/invite.tsx | 20 + pages/bots/[id]/vote.tsx | 4 +- pages/{ => bots}/categories/[category].tsx | 10 +- pages/{ => bots}/categories/index.tsx | 10 +- pages/bots/index.tsx | 72 ++++ pages/{ => bots}/list/new.tsx | 6 +- pages/{ => bots}/list/votes.tsx | 12 +- pages/bots/search.tsx | 88 ++++ pages/developers/applications/bots/[id].tsx | 4 +- pages/developers/applications/index.tsx | 13 +- .../developers/applications/servers/[id].tsx | 163 +++++++ pages/index.tsx | 46 +- pages/license.tsx | 2 +- pages/panel.tsx | 14 +- pages/pendingBots/[id]/[date].tsx | 2 +- pages/search.tsx | 77 ++-- pages/security.tsx | 10 +- pages/servers/[id]/edit.tsx | 196 +++++++++ pages/servers/[id]/index.tsx | 319 ++++++++++++++ pages/servers/[id]/join.tsx | 19 + pages/servers/[id]/report.tsx | 140 ++++++ pages/servers/[id]/vote.tsx | 135 ++++++ pages/servers/categories/[category].tsx | 62 +++ pages/servers/categories/index.tsx | 30 ++ pages/servers/index.tsx | 58 +++ pages/servers/list/votes.tsx | 63 +++ pages/servers/search.tsx | 88 ++++ pages/tos.tsx | 2 +- pages/users/[id]/index.tsx | 17 +- pages/verification.tsx | 4 +- public/manifest.json | 2 +- public/opensearch.xml | 2 +- types/index.ts | 117 ++++- utils/Constants.ts | 101 ++++- utils/Mongo.ts | 29 ++ utils/Query.ts | 402 ++++++++++++++++-- utils/Tools.ts | 23 +- utils/Yup.ts | 124 +++++- yarn.lock | 164 ++++++- 79 files changed, 3940 insertions(+), 400 deletions(-) create mode 100644 .github/workflows/publish.yml create mode 100644 components/Image.tsx create mode 100644 components/ServerCard.tsx create mode 100644 components/ServerIcon.tsx create mode 100644 pages/addserver/[id].tsx create mode 100644 pages/addserver/index.tsx create mode 100644 pages/api/image/discord/icons/[id].ts create mode 100644 pages/api/v2/applications/servers/[id]/reset.ts create mode 100644 pages/api/v2/search/all.ts create mode 100644 pages/api/v2/search/servers.ts create mode 100644 pages/api/v2/servers/[id]/index.ts create mode 100644 pages/api/v2/servers/[id]/owners.ts create mode 100644 pages/api/v2/servers/[id]/report.ts create mode 100644 pages/api/v2/servers/[id]/vote.ts create mode 100644 pages/api/widget/servers/[type]/[id].ts create mode 100644 pages/bots/[id]/invite.tsx rename pages/{ => bots}/categories/[category].tsx (90%) rename pages/{ => bots}/categories/index.tsx (79%) create mode 100644 pages/bots/index.tsx rename pages/{ => bots}/list/new.tsx (82%) rename pages/{ => bots}/list/votes.tsx (86%) create mode 100644 pages/bots/search.tsx create mode 100644 pages/developers/applications/servers/[id].tsx create mode 100644 pages/servers/[id]/edit.tsx create mode 100644 pages/servers/[id]/index.tsx create mode 100644 pages/servers/[id]/join.tsx create mode 100644 pages/servers/[id]/report.tsx create mode 100644 pages/servers/[id]/vote.tsx create mode 100644 pages/servers/categories/[category].tsx create mode 100644 pages/servers/categories/index.tsx create mode 100644 pages/servers/index.tsx create mode 100644 pages/servers/list/votes.tsx create mode 100644 pages/servers/search.tsx create mode 100644 utils/Mongo.ts 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 }) => { +
    + + + +