Skip to content

Commit

Permalink
Feature/serverlist skeleton (#468)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* 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: koreanbots/serverlist-testing#10

* fix(Query): not using vote type

close: koreanbots/serverlist-testing#9

* fix(Constants): fixed category unexpected char

close: koreanbots/serverlist-testing#8

* fix(Query): serialize server data

* fix(Query): returning null on boost level 0

* fix(page/servers): displaying n/a on boostTier null

close: koreanbots/serverlist-testing#4

* fix(pages/servers): hiding emoji list if no emoji

close: koreanbots/serverlist-testing#1

* typo(pages/servers): bot to server

close: koreanbots/serverlist-testing#2

* fix(components/Hero): editing vote list link

close: koreanbots/serverlist-testing#11

* chore(*): changed list route

* feat(pages/servers/list/votes): added server vote list page

close: koreanbots/serverlist-testing#12

* feat(Dockerfile): added pre-build

* fix(Image): image broken when fallbackSrc not given

close: koreanbots/serverlist-testing#5

* ci: checking out submodules

* fix(ServerCard): bot category displayed at ServerCard

close: koreanbots/serverlist-testing#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: koreanbots/serverlist-testing#19

* fix(utils/Query): returning date as string

close: koreanbots/serverlist-testing#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 <[email protected]>
Co-authored-by: Ryu JuHeon <[email protected]>
  • Loading branch information
3 people authored Nov 6, 2021
1 parent 1fbb685 commit 678fae4
Show file tree
Hide file tree
Showing 79 changed files with 3,935 additions and 395 deletions.
5 changes: 5 additions & 0 deletions .env.demo.local
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
43 changes: 43 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -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
59 changes: 14 additions & 45 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
env:
CI: true
test:
name: Run Test
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand All @@ -34,56 +34,25 @@ jobs:
node-version: 14
- name: yarn install
run: yarn install
- name: Setup MySQL
uses: getong/[email protected]
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
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: |
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
- name: Build
run: yarn build
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
9 changes: 7 additions & 2 deletions components/Application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@ 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<ApplicationProps> = ({ type, id, name }) => {
return <Link href={`/developers/applications/${type + 's'}/${id}`}>
<div className='relative px-2 py-4 text-center dark:bg-discord-black bg-little-white rounded-lg cursor-pointer transform hover:-translate-y-1 transition duration-100 ease-in'>
<DiscordAvatar userID={id} className='px-2 w-full rounded-xl' />
{
type === 'bot' ?
<DiscordAvatar userID={id} className='px-2 w-full rounded-xl' /> :
<ServerIcon id={id} className='px-2 w-full rounded-xl' />
}
<h2 className='pt-2 whitespace-nowrap text-xl font-medium truncate'>{name}</h2>
</div>
</Link>

}

interface ApplicationProps {
type: 'bot'
type: 'bot' | 'server'
id: string
name: string
}
Expand Down
5 changes: 2 additions & 3 deletions components/BotCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const BotCard: React.FC<BotCardProps> = ({ manage = false, bot }) => {
<div>
<div className='category flex flex-wrap px-2'>
{bot.category.slice(0, 3).map(el => (
<Tag key={el} text={el} href={`/categories/${el}`} dark />
<Tag key={el} text={el} href={`/bots/categories/${el}`} dark />
))}{' '}
{bot.category.length > 3 && <Tag text={`+${bot.category.length - 3}`} dark />}
</div>
Expand Down Expand Up @@ -96,8 +96,7 @@ const BotCard: React.FC<BotCardProps> = ({ manage = false, bot }) => {
</a> :
<a
href={
bot.url ||
`https://discordapp.com/oauth2/authorize?client_id=${bot.id}&scope=bot&permissions=0`
makeBotURL(bot) + '/invite'
}
rel='noopener noreferrer'
target='_blank'
Expand Down
6 changes: 3 additions & 3 deletions components/DeveloperLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ const DeveloperLayout: React.FC<DeveloperLayout> = ({ children, enabled, docs, c
const [ navbarEnabled, setNavbarOpen ] = useState(false)

return <div className='flex min-h-screen'>
<NextSeo title='한디리 개발자' description='한국 디스코드봇 리스트 API를 활용하여 봇에 다양한 기능을 추가해보세요.' openGraph={{
<NextSeo title='한디리 개발자' description='한국 디스코드 리스트 API를 활용하여 봇에 다양한 기능을 추가해보세요.' openGraph={{
title:'한디리 개발자',
description:'한국 디스코드봇 리스트 API를 활용하여 봇에 다양한 기능을 추가해보세요.'
description:'한국 디스코드 리스트 API를 활용하여 봇에 다양한 기능을 추가해보세요.'
}} />
<div className='block lg:hidden h-screen relative'>
<div className='w-18 pt-20 px-2 h-full text-center bg-little-white dark:bg-discord-black fixed'>
Expand Down Expand Up @@ -43,7 +43,7 @@ const DeveloperLayout: React.FC<DeveloperLayout> = ({ children, enabled, docs, c
<Divider className='lg:hidden' />
<Link href='/developers/applications'>
<li className={`cursor-pointer py-2 px-4 rounded-md ${enabled === 'applications' ? 'bg-discord-blurple text-white' : 'hover:text-gray-500 dark:hover:text-white'}`}>
나의
나의 리스트
</li>
</Link>
<Link href='/developers/docs'>
Expand Down
46 changes: 9 additions & 37 deletions components/DiscordAvatar.tsx
Original file line number Diff line number Diff line change
@@ -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<DiscordAvatarProps> = props => {
const fallback = '/img/default.png'
const [ webpUnavailable, setWebpUnavailable ] = useState<boolean>()

useEffect(()=> {
setWebpUnavailable(localStorage.webp === 'false')
}, [])

return <img
alt={props.alt ?? 'Image'}
loading='lazy'
className={props.className}
src={
KoreanbotsEndPoints.CDN.avatar(props.userID, { format: !webpUnavailable ? 'webp' : 'png', size: props.size ?? 256})
}
onError={(e: SyntheticEvent<HTMLImageElement, ImageEvent>)=> {
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 <Image
{...props}
src={KoreanbotsEndPoints.CDN.avatar(props.userID, { format: 'webp', size: props.size ?? 256})}
fallbackSrc={KoreanbotsEndPoints.CDN.avatar(props.userID, { format: 'png', size: props.size ?? 256})}
/>

}

interface DiscordAvatarProps {
Expand Down
6 changes: 3 additions & 3 deletions components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ const Footer: React.FC<FooterProps> = ({ theme, setTheme }) => {
<div className='bottom-0 text-white bg-discord-black py-24'>
<Container className='w-11/12 lg:flex lg:pt-0 lg:w-4/5' ignoreColor>
<div className='w-full lg:w-2/5'>
<h1 className='text-koreanbots-blue text-3xl font-bold'>국내 디스코드 봇을 한 곳에서.</h1>
<span className='text-base'>2020-2021 Koreanbots, All rights reserved.</span>
<h1 className='text-koreanbots-blue text-2xl font-bold'>국내 디스코드의 모든 것을 한 곳에서.</h1>
<span className='text-base'>2020-2021 한국 디스코드 리스트, All rights reserved.</span>
<div className='text-2xl'>
<Link href='/discord'>
<a className='mr-2'>
Expand All @@ -30,7 +30,7 @@ const Footer: React.FC<FooterProps> = ({ theme, setTheme }) => {
</div>
<div className='grid flex-grow gap-2 grid-cols-2 md:grid-cols-7'>
<div className='col-span-2 mb-2'>
<h2 className='text-koreanbots-blue text-base font-bold'>한국 디스코드봇 리스트</h2>
<h2 className='text-koreanbots-blue text-base font-bold'>한국 디스코드 리스트</h2>
<ul className='text-sm'>
<li>
<Link href='/about'>
Expand Down
52 changes: 38 additions & 14 deletions components/Hero.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import dynamic from 'next/dynamic'
import { NextSeo } from 'next-seo'

import { categories, categoryIcon } from '@utils/Constants'
import { botCategories, botCategoryIcon, serverCategories, serverCategoryIcon } from '@utils/Constants'

const Container = dynamic(()=> import('@components/Container'))
const Tag = dynamic(()=> import('@components/Tag'))
const Search = dynamic(()=> import('@components/Search'))

const Hero:React.FC<HeroProps> = ({ header, description }) => {
const Hero:React.FC<HeroProps> = ({ type='all', header, description }) => {
const link = `/${type}/categories`
return <>
<NextSeo title={header} description={description} openGraph={{
title: header,
Expand All @@ -16,30 +17,53 @@ const Hero:React.FC<HeroProps> = ({ header, description }) => {
<div className='dark:bg-discord-black bg-discord-blurple text-gray-100 md:p-0 mb-8'>
<Container className='pt-24 pb-16 md:pb-20' ignoreColor>
<h1 className='hidden md:block text-left text-3xl font-bold'>
{ header && `${header} - `}한국 디스코드봇 리스트
{ header && `${header} - `}한국 디스코드 리스트
</h1>
<h1 className='md:hidden text-center text-3xl font-semibold'>
{ header && <span className='text-4xl'>{header}<br/></span>}한국 디스코드봇 리스트
{ header && <span className='text-4xl'>{header}<br/></span>}한국 디스코드 리스트
</h1>
<p className='text-center sm:text-left text-xl font-base mt-2'>{description || '다양한 국내 디스코드봇을 한곳에서 확인하세요!'}</p>
<p className='text-center sm:text-left text-xl font-base mt-2'>{description || `${type !== 'all' ? '다양한 ' : ''}국내 디스코드${{ all: '의 모든 것을', bots: ' 봇들을', servers: ' 서버들을' }[type]} 한 곳에서 확인하세요!`}</p>
<Search />
<div className='flex flex-wrap mt-5'>
<Tag key='list' text={<>
<i className='fas fa-heart text-red-600'/> 하트 랭킹
</>} dark bigger href='/list/votes' />
{ categories.slice(0, 4).map(t=> <Tag key={t} text={<>
<i className={categoryIcon[t]} /> {t}
</>} dark bigger href={`/categories/${t}`} />) }
<Tag key='tag' text={<>
<i className='fas fa-tag'/> 카테고리 더보기
</>} dark bigger href='/categories' />
{
type === 'all' ? <>
<Tag text={
<>
<i className='fas fa-robot text-koreanbots-blue'/> 봇 리스트
</>
} dark bigger href='/bots' />
<Tag text={
<>
<i className='fas fa-users text-koreanbots-blue'/> 서버 리스트
</>
} dark bigger href='/servers' />
{
botCategories.slice(0, 2).map(t => <Tag key={t} text={<><i className={botCategoryIcon[t]} /> {t}</>} dark bigger href={`/bots/categories/${t}`} />)
}

{
serverCategories.slice(0, 2).map(t => <Tag key={t} text={<><i className={serverCategoryIcon[t]} /> {t} 서버</>} dark bigger href={`/servers/categories/${t}`} />)
}
</>: <>
<Tag key='list' text={<>
<i className='fas fa-heart text-red-600'/> 하트 랭킹
</>} dark bigger href={type === 'bots' ? '/bots/list/votes' : '/servers/list/votes'} />
{ (type === 'bots' ? botCategories : serverCategories).slice(0, 4).map(t=> <Tag key={t} text={<>
<i className={(type === 'bots' ? botCategoryIcon : serverCategoryIcon)[t]} /> {t}
</>} dark bigger href={`${link}/${t}`} />) }
<Tag key='tag' text={<>
<i className='fas fa-tag'/> 카테고리 더보기
</>} dark bigger href={link} />
</>
}
</div>
</Container>
</div>
</>
}

interface HeroProps {
type?: 'all' | 'bots' | 'servers'
header?: string
description?: string
}
Expand Down
Loading

0 comments on commit 678fae4

Please sign in to comment.