mirror of
https://github.com/koreanbots/core.git
synced 2025-12-15 06:10:22 +00:00
refactor: using next-seo for seo
This commit is contained in:
parent
84e6311a83
commit
f4ba609f71
@ -4,16 +4,16 @@ import Link from 'next/link'
|
||||
import { ReactNode, useState } from 'react'
|
||||
|
||||
import { DocsData } from '@types'
|
||||
import { NextSeo } from 'next-seo'
|
||||
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
const Divider = dynamic(() => import('@components/Divider'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
|
||||
const DeveloperLayout: React.FC<DeveloperLayout> = ({ children, enabled, docs, currentDoc }:DeveloperLayout) => {
|
||||
const [ navbarEnabled, setNavbarOpen ] = useState(false)
|
||||
|
||||
return <div className='flex min-h-screen'>
|
||||
<SEO title='한디리 개발자' description='한국 디스코드봇 리스트 API를 활용하여 봇에 다양한 기능을 추가해보세요.' />
|
||||
<NextSeo title='한디리 개발자' 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'>
|
||||
<ul className='text-gray-600 dark:text-gray-300'>
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
import { NextSeo } from 'next-seo'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
|
||||
const Docs: React.FC<DocsProps> = ({ title, header, description, subheader, children }) => {
|
||||
return (
|
||||
<>
|
||||
<SEO
|
||||
title={typeof header === 'string' ? header : title}
|
||||
description={description || subheader}
|
||||
/>
|
||||
<NextSeo title={typeof header === 'string' ? header : title} description={description || subheader}/>
|
||||
<div className='dark:bg-discord-black bg-discord-blurple z-20'>
|
||||
<Container className='py-20' ignoreColor>
|
||||
<h1 className='mt-10 text-center text-gray-100 text-4xl font-bold sm:text-left'>
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useRouter } from 'next/router'
|
||||
import { NextSeo } from 'next-seo'
|
||||
|
||||
import { ErrorText } from '@utils/Constants'
|
||||
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const Button = dynamic(() => import('@components/Button'))
|
||||
|
||||
const Forbidden:React.FC = () => {
|
||||
const router = useRouter()
|
||||
return <>
|
||||
<SEO title='권한이 없습니다' />
|
||||
<NextSeo title='권한이 없습니다' />
|
||||
<div className='flex items-center justify-center h-screen select-none'>
|
||||
<div className='container mx-auto px-20 md:text-left text-center'>
|
||||
<h1 className='text-8xl font-semibold'>403</h1>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import dynamic from 'next/dynamic'
|
||||
import { NextSeo } from 'next-seo'
|
||||
|
||||
import { categories, categoryIcon } from '@utils/Constants'
|
||||
|
||||
@ -8,6 +9,7 @@ const Search = dynamic(()=> import('@components/Search'))
|
||||
|
||||
const Hero:React.FC<HeroProps> = ({ header, description }) => {
|
||||
return <>
|
||||
<NextSeo title={header} description={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'>
|
||||
|
||||
@ -5,7 +5,6 @@ const SEO: React.FC<SEOProps> = ({ title, description, image }: SEOProps) => {
|
||||
<Head>
|
||||
<title>{title} - 한국 디스코드봇 리스트</title>
|
||||
{description && <meta name='description' content={description} />}
|
||||
<meta name='og:site_name' content='한국 디스코드봇 리스트' />
|
||||
<meta name='og:title' content={title} />
|
||||
{description && <meta name='og:description' content={description} />}
|
||||
{image && <meta name='og:image' content={image} />}
|
||||
|
||||
@ -42,6 +42,7 @@
|
||||
"next": "10.2.0",
|
||||
"next-connect": "0.10.1",
|
||||
"next-pwa": "5.2.21",
|
||||
"next-seo": "4.24.0",
|
||||
"next-session": "3.4.0",
|
||||
"node-emoji": "1.10.0",
|
||||
"nprogress": "0.2.0",
|
||||
|
||||
@ -3,6 +3,7 @@ import App, { AppContext, AppProps } from 'next/app'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { Router, useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { DefaultSeo } from 'next-seo'
|
||||
import { GlobalHotKeys } from 'react-hotkeys'
|
||||
import NProgress from 'nprogress'
|
||||
|
||||
@ -59,17 +60,36 @@ const KoreanbotsApp = ({ Component, pageProps, err, cookie }: KoreanbotsProps):
|
||||
}, [])
|
||||
|
||||
return <div className={theme}>
|
||||
<DefaultSeo
|
||||
titleTemplate='%s - 한국 디스코드봇 리스트'
|
||||
defaultTitle={TITLE}
|
||||
description={DESCRIPTION}
|
||||
openGraph={{
|
||||
type: 'website',
|
||||
title: TITLE,
|
||||
url: 'https://koreanbots.dev',
|
||||
site_name: TITLE,
|
||||
description: DESCRIPTION,
|
||||
images: [
|
||||
{
|
||||
url: '/logo.png',
|
||||
width: 300,
|
||||
height: 300,
|
||||
alt: 'Logo'
|
||||
}
|
||||
]
|
||||
}}
|
||||
twitter={{
|
||||
site: '@koreanbots',
|
||||
handle: '@koreanbots',
|
||||
cardType: 'summary'
|
||||
}}
|
||||
/>
|
||||
<Head>
|
||||
<title>{TITLE}</title>
|
||||
{/* META */}
|
||||
<meta charSet='utf-8' />
|
||||
<meta httpEquiv='X-UA-Compatible' content='IE=edge' />
|
||||
<meta name='description' content={DESCRIPTION} />
|
||||
<meta name='keywords' content='Korea, Korean, Discord, Bot, 디스코드봇, 한디리' />
|
||||
<meta name='og:title' content={TITLE} />
|
||||
<meta name='og:url' content='https://koreanbots.dev' />
|
||||
<meta name='og:description' content={DESCRIPTION} />
|
||||
<meta name='og:image' content='/favicon.ico' />
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no' />
|
||||
|
||||
{/* Android */}
|
||||
|
||||
@ -3,6 +3,7 @@ import { useRef, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Link from 'next/link'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import { Form, Formik } from 'formik'
|
||||
import HCaptcha from '@hcaptcha/react-hcaptcha'
|
||||
|
||||
@ -10,9 +11,9 @@ 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 { ResponseProps, SubmittedBot, Theme, User } from '@types'
|
||||
import { getToken } from '@utils/Csrf'
|
||||
import Fetch from '@utils/Fetch'
|
||||
import { ResponseProps, SubmittedBot, Theme, User } from '@types'
|
||||
|
||||
const CheckBox = dynamic(() => import('@components/Form/CheckBox'))
|
||||
const Label = dynamic(() => import('@components/Form/Label'))
|
||||
@ -27,7 +28,6 @@ const Button = dynamic(() => import('@components/Button'))
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
const Message = dynamic(() => import('@components/Message'))
|
||||
const Captcha = dynamic(() => import('@components/Captcha'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
|
||||
const AddBot:NextPage<AddBotProps> = ({ logged, user, csrfToken, theme }) => {
|
||||
const [ data, setData ] = useState<ResponseProps<SubmittedBot>>(null)
|
||||
@ -68,10 +68,10 @@ const AddBot:NextPage<AddBotProps> = ({ logged, user, csrfToken, theme }) => {
|
||||
}
|
||||
if(!logged) {
|
||||
toLogin()
|
||||
return <SEO title='새로운 봇 추가하기' description='자신의 봇을 한국 디스코드봇 리스트에 등록하세요.'/>
|
||||
return <NextSeo title='새로운 봇 추가하기' description='자신의 봇을 한국 디스코드봇 리스트에 등록하세요.' />
|
||||
}
|
||||
return <Container paddingTop className='py-5'>
|
||||
<SEO title='새로운 봇 추가하기' description='자신의 봇을 한국 디스코드봇 리스트에 등록하세요.'/>
|
||||
<NextSeo title='새로운 봇 추가하기' description='자신의 봇을 한국 디스코드봇 리스트에 등록하세요.' />
|
||||
<h1 className='text-3xl font-bold'>새로운 봇 추가하기</h1>
|
||||
<div className='mt-1 mb-5'>
|
||||
안녕하세요, <span className='font-semibold'>{user.username}#{user.tag}</span>님! <a role='button' tabIndex={0} onKeyDown={toLogin} onClick={toLogin} className='text-discord-blurple cursor-pointer outline-none'>본인이 아니신가요?</a>
|
||||
|
||||
@ -2,6 +2,7 @@ import { NextPage, NextPageContext } from 'next'
|
||||
import { useRouter } from 'next/router'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Link from 'next/link'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import Tooltip from 'rc-tooltip'
|
||||
@ -26,7 +27,6 @@ const Divider = dynamic(() => import('@components/Divider'))
|
||||
const Tag = dynamic(() => import('@components/Tag'))
|
||||
const Owner = dynamic(() => import('@components/Owner'))
|
||||
const Segment = dynamic(() => import('@components/Segment'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const LongButton = dynamic(() => import('@components/LongButton'))
|
||||
const Advertisement = dynamic(() => import('@components/Advertisement'))
|
||||
const Markdown = dynamic(() => import ('@components/Markdown'))
|
||||
@ -49,10 +49,22 @@ const Bots: NextPage<BotsProps> = ({ data, desc, date, user, theme, csrfToken })
|
||||
if (!data?.id) return <NotFound />
|
||||
return <div style={bg ? { background: `linear-gradient(to right, rgba(34, 36, 38, 0.68), rgba(34, 36, 38, 0.68)), url("${data.bg}") center top / cover no-repeat fixed` } : {}}>
|
||||
<Container paddingTop className='py-10'>
|
||||
<SEO
|
||||
<NextSeo
|
||||
title={data.name}
|
||||
description={data.intro}
|
||||
image={KoreanbotsEndPoints.OG.bot(data.id, data.name, data.intro, data.category, [formatNumber(data.votes), formatNumber(data.servers)])}
|
||||
twitter={{
|
||||
cardType: 'summary_large_image'
|
||||
}}
|
||||
openGraph={{
|
||||
images: [
|
||||
{
|
||||
url: KoreanbotsEndPoints.OG.bot(data.id, data.name, data.intro, data.category, [formatNumber(data.votes), formatNumber(data.servers)]),
|
||||
width: 2048,
|
||||
height: 1170,
|
||||
alt: 'Bot Preview Image'
|
||||
}
|
||||
]
|
||||
}}
|
||||
/>
|
||||
{
|
||||
data.state === 'blocked' ? <div className='pb-40'>
|
||||
@ -120,7 +132,7 @@ const Bots: NextPage<BotsProps> = ({ data, desc, date, user, theme, csrfToken })
|
||||
</h4>
|
||||
</LongButton>
|
||||
}
|
||||
<Link href={{ pathname: `/bots/${router.query.id}/vote`, query: { csrfToken } }}>
|
||||
<Link href={`/bots/${router.query.id}/vote`}>
|
||||
<LongButton>
|
||||
<h4>
|
||||
<i className='fas fa-heart text-red-600' /> 하트 추가
|
||||
|
||||
@ -17,6 +17,7 @@ import Fetch from '@utils/Fetch'
|
||||
import Day from '@utils/Day'
|
||||
import { getJosaPicker } from 'josa'
|
||||
import { KoreanbotsEndPoints } from '@utils/Constants'
|
||||
import { NextSeo } from 'next-seo'
|
||||
|
||||
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
@ -24,26 +25,39 @@ const DiscordAvatar = dynamic(() => import('@components/DiscordAvatar'))
|
||||
const Button = dynamic(() => import('@components/Button'))
|
||||
const Tag = dynamic(() => import('@components/Tag'))
|
||||
const Segment = dynamic(() => import('@components/Segment'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const Advertisement = dynamic(() => import('@components/Advertisement'))
|
||||
const Login = dynamic(() => import('@components/Login'))
|
||||
|
||||
const VoteBot: NextPage<VoteBotProps> = ({ data, user, csrfToken, theme }) => {
|
||||
const VoteBot: NextPage<VoteBotProps> = ({ data, user, theme, csrfToken }) => {
|
||||
const [ votingStatus, setVotingStatus ] = useState(0)
|
||||
const [ result, setResult ] = useState<ResponseProps<{retryAfter?: number}>>(null)
|
||||
const router = useRouter()
|
||||
if(!data?.id) return <NotFound />
|
||||
if(typeof window !== 'undefined' && csrfToken !== router.query.csrfToken) {
|
||||
router.push(`/bots/${data.id}`)
|
||||
return <SEO title={data.name} description={`한국 디스코드봇 리스트에서 ${data.name}에 투표하세요.`} image={KoreanbotsEndPoints.CDN.avatar(data.id, { format: 'png', size: 256 })} />
|
||||
}
|
||||
if(!user) return <Login>
|
||||
<SEO title={data.name} description={`한국 디스코드봇 리스트에서 ${data.name}에 투표하세요.`} image={KoreanbotsEndPoints.CDN.avatar(data.id, { format: 'png', size: 256 })} />
|
||||
<NextSeo title={data.name} description={`한국 디스코드봇 리스트에서 ${data.name}에 투표하세요.`} openGraph={{
|
||||
images: [
|
||||
{
|
||||
url: KoreanbotsEndPoints.CDN.avatar(data.id, { format: 'png', size: 256 }),
|
||||
width: 256,
|
||||
height: 256,
|
||||
alt: 'Bot Avatar'
|
||||
}
|
||||
]
|
||||
}} />
|
||||
</Login>
|
||||
|
||||
if((checkBotFlag(data.flags, 'trusted') || checkBotFlag(data.flags, 'partnered')) && data.vanity && data.vanity !== router.query.id) router.push(`/bots/${data.vanity}/vote?csrfToken=${csrfToken}`)
|
||||
return <Container paddingTop className='py-10'>
|
||||
<SEO title={data.name} description={`한국 디스코드봇 리스트에서 ${data.name}에 투표하세요.`} image={KoreanbotsEndPoints.CDN.avatar(data.id, { format: 'png', size: 256 })} />
|
||||
<NextSeo title={data.name} description={`한국 디스코드봇 리스트에서 ${data.name}에 투표하세요.`} openGraph={{
|
||||
images: [
|
||||
{
|
||||
url: KoreanbotsEndPoints.CDN.avatar(data.id, { format: 'png', size: 256 }),
|
||||
width: 256,
|
||||
height: 256,
|
||||
alt: 'Bot Avatar'
|
||||
}
|
||||
]
|
||||
}} />
|
||||
<Advertisement />
|
||||
<Link href={makeBotURL(data)}>
|
||||
<a className='text-blue-500 hover:opacity-80'><i className='fas fa-arrow-left mt-3 mb-3' /> <strong>{data.name}</strong>{getJosaPicker('로')(data.name)} 돌아가기</a>
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { GetServerSideProps, NextPage } from 'next'
|
||||
import { useState } from 'react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { DiscordEnpoints, GuildPermissions } from '@utils/Constants'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import { Formik, Form } from 'formik'
|
||||
import { ParsedUrlQuery } from 'node:querystring'
|
||||
import { ParsedUrlQuery } from 'querystring'
|
||||
|
||||
import { DiscordEnpoints, GuildPermissions } from '@utils/Constants'
|
||||
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
const Input = dynamic(() => import('@components/Form/Input'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
|
||||
const Calculator:NextPage<CalculatorProps> = ({ query }) => {
|
||||
const [ value, setValue ] = useState<{[perm: string]: boolean}>({})
|
||||
@ -23,7 +24,7 @@ const Calculator:NextPage<CalculatorProps> = ({ query }) => {
|
||||
|
||||
}
|
||||
return <Container paddingTop className='pb-10'>
|
||||
<SEO title='봇 초대링크 생성기' description='디스코드 봇 초대링크를 간편하게 생성하세요' />
|
||||
<NextSeo title='봇 초대링크 생성기' description='디스코드 봇 초대링크를 간편하게 생성하세요' />
|
||||
<h1 className='text-4xl font-bold mt-2 mb-4'>봇 초대링크 생성기</h1>
|
||||
<div className='text-2xl font-bold inline-flex items-center'>권한: {Object.keys(value).filter(el => value[el]).map(el => Number(el)).reduce((prev, curr) => prev | curr, 0)}
|
||||
<span className='ml-2 text-lg font-semibold'>= { Object.keys(value).filter(el => value[el]).map(el => `0x${Number(el).toString(16)}`).join(' | ') }</span>
|
||||
|
||||
@ -11,7 +11,6 @@ import { useRouter } from 'next/router'
|
||||
|
||||
const Hero = dynamic(() => import('@components/Hero'))
|
||||
const Advertisement = dynamic(() => import('@components/Advertisement'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const ResponsiveGrid = dynamic(() => import('@components/ResponsiveGrid'))
|
||||
const BotCard = dynamic(() => import('@components/BotCard'))
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
@ -29,7 +28,6 @@ const Category: NextPage<CategoryProps> = ({ data, query }) => {
|
||||
if(!data || data.data.length === 0 || data.totalPage < Number(query.page)) return <NotFound message={data?.data.length === 0 ? '해당 카테고리에 해당되는 봇이 존재하지 않습니다.' : null} />
|
||||
return <>
|
||||
<Hero header={`${query.category} 카테고리 봇들`} description={`다양한 "${query.category}" 카테고리의 봇들을 만나보세요.`} />
|
||||
<SEO title={`${query.category} 카테고리 봇들`} description={`다양한 ${query.category} 카테고리의 봇들을 만나보세요.`} />
|
||||
{
|
||||
query.category === 'NSFW' && !nsfw ? <NSFW onClick={() => setNSFW(true)} onDisableClick={() => localStorage.nsfw = true} />
|
||||
: <Container>
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
import { NextPage } from 'next'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { NextSeo } from 'next-seo'
|
||||
|
||||
import { categories, categoryIcon } from '@utils/Constants'
|
||||
|
||||
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
const Advertisement = dynamic(() => import('@components/Advertisement'))
|
||||
const Tag = dynamic(() => import('@components/Tag'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const Segment = dynamic(() => import('@components/Segment'))
|
||||
|
||||
const Categories:NextPage = () => {
|
||||
return <Container paddingTop>
|
||||
<SEO title='전체 카테고리' description='한국 디스코드봇 리스트의 전체 카테고리입니다.'/>
|
||||
<NextSeo title='전체 카테고리' description='한국 디스코드봇 리스트의 전체 카테고리입니다.' />
|
||||
<h1 className='text-2xl font-bold mt-2 mb-5'>전체 카테고리</h1>
|
||||
<Segment className='mb-10'>
|
||||
<div className='text-center flex flex-wrap mt-1.5'>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { NextPage, NextPageContext } from 'next'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { NextSeo } from 'next-seo'
|
||||
|
||||
import { get } from '@utils/Query'
|
||||
import { parseCookie } from '@utils/Tools'
|
||||
@ -8,12 +9,11 @@ import { Bot, User } from '@types'
|
||||
|
||||
const Application = dynamic(() => import('@components/Application'))
|
||||
const DeveloperLayout = dynamic(() => import('@components/DeveloperLayout'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const Login = dynamic(() => import('@components/Login'))
|
||||
|
||||
const Applications: NextPage<ApplicationsProps> = ({ user }) => {
|
||||
if(!user) return <Login>
|
||||
<SEO title='한디리 개발자' description='한국 디스코드봇 리스트 API를 활용하여 봇에 다양한 기능을 추가해보세요.' />
|
||||
<NextSeo title='한디리 개발자' description='한국 디스코드봇 리스트 API를 활용하여 봇에 다양한 기능을 추가해보세요.' />
|
||||
</Login>
|
||||
return <DeveloperLayout enabled='applications'>
|
||||
<h1 className='text-3xl font-bold'>나의 봇</h1>
|
||||
|
||||
@ -6,7 +6,6 @@ import { BotList } from '@types'
|
||||
|
||||
const Hero = dynamic(() => import('@components/Hero'))
|
||||
const Advertisement = dynamic(() => import('@components/Advertisement'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const BotCard = dynamic(() => import('@components/BotCard'))
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
const ResponsiveGrid = dynamic(() => import('@components/ResponsiveGrid'))
|
||||
@ -14,7 +13,6 @@ const ResponsiveGrid = dynamic(() => import('@components/ResponsiveGrid'))
|
||||
const New:NextPage<NewProps> = ({ data }) => {
|
||||
return <>
|
||||
<Hero header='새로운 봇' description='최근에 한국 디스코드봇 리스트에 추가된 봇들입니다!' />
|
||||
<SEO title='새로운 봇' description='최근에 추가된 봇들입니다!' />
|
||||
<Container className='pb-10'>
|
||||
<Advertisement />
|
||||
<ResponsiveGrid>
|
||||
|
||||
@ -11,7 +11,6 @@ import { PageCount } from '@utils/Yup'
|
||||
|
||||
const Hero = dynamic(() => import('@components/Hero'))
|
||||
const Advertisement = dynamic(() => import('@components/Advertisement'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const BotCard = dynamic(() => import('@components/BotCard'))
|
||||
const ResponsiveGrid = dynamic(() => import('@components/ResponsiveGrid'))
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
@ -22,7 +21,6 @@ const Votes:NextPage<VotesProps> = ({ data }) => {
|
||||
if(!data || data.data.length === 0 || data.totalPage < Number(router.query.page)) return <NotFound />
|
||||
return <>
|
||||
<Hero header='하트 랭킹' description='하트를 많이 받은 봇들의 순위입니다!'/>
|
||||
<SEO title='하트 랭킹' description='하트를 많이 받은 봇들의 순위입니다!'/>
|
||||
<section id='list'>
|
||||
<Container className='pb-10'>
|
||||
<Advertisement />
|
||||
|
||||
@ -3,8 +3,9 @@ import { useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Link from 'next/link'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import { Form, Formik } from 'formik'
|
||||
import { ParsedUrlQuery } from 'node:querystring'
|
||||
import { ParsedUrlQuery } from 'querystring'
|
||||
import { getJosaPicker } from 'josa'
|
||||
|
||||
import { get } from '@utils/Query'
|
||||
@ -34,7 +35,6 @@ const Tag = dynamic(() => import('@components/Tag'))
|
||||
const Message = dynamic(() => import('@components/Message'))
|
||||
const Modal = dynamic(() => import('@components/Modal'))
|
||||
const Captcha = dynamic(() => import('@components/Captcha'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const Login = dynamic(() => import('@components/Login'))
|
||||
|
||||
const ManageBotPage:NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme }) => {
|
||||
@ -57,11 +57,11 @@ const ManageBotPage:NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme })
|
||||
|
||||
if(!bot) return <NotFound />
|
||||
if(!user) return <Login>
|
||||
<SEO title='봇 정보 수정하기' description='봇의 정보를 수정합니다.'/>
|
||||
<NextSeo title='봇 정보 수정하기' description='봇의 정보를 수정합니다.'/>
|
||||
</Login>
|
||||
if(!(bot.owners as User[]).find(el => el.id === user.id) && !checkUserFlag(user.flags, 'staff')) return <Forbidden />
|
||||
return <Container paddingTop className='pt-5 pb-10'>
|
||||
<SEO title={`${bot.name} 수정하기`} description='봇의 정보를 수정합니다.'/>
|
||||
<NextSeo title={`${bot.name} 수정하기`} description='봇의 정보를 수정합니다.'/>
|
||||
<h1 className='text-3xl font-bold mb-8'>봇 관리하기</h1>
|
||||
<Formik initialValues={cleanObject({
|
||||
agree: false,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { NextPage, NextPageContext } from 'next'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import { useState } from 'react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useRouter } from 'next/router'
|
||||
@ -10,7 +11,6 @@ import Fetch from '@utils/Fetch'
|
||||
import { getToken } from '@utils/Csrf'
|
||||
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const ResponsiveGrid = dynamic(() => import('@components/ResponsiveGrid'))
|
||||
const Button = dynamic(() => import('@components/Button'))
|
||||
const BotCard = dynamic(() => import('@components/BotCard'))
|
||||
@ -21,10 +21,10 @@ const Panel:NextPage<PanelProps> = ({ logged, user, submits, csrfToken }) => {
|
||||
const router = useRouter()
|
||||
const [ submitLimit, setSubmitLimit ] = useState(8)
|
||||
if(!logged) return <Login>
|
||||
<SEO title='관리 패널' />
|
||||
<NextSeo title='관리 패널' />
|
||||
</Login>
|
||||
return <Container paddingTop className='pt-5 pb-10'>
|
||||
<SEO title='관리 패널' />
|
||||
<NextSeo title='관리 패널' />
|
||||
<h1 className='text-4xl font-bold'>관리 패널</h1>
|
||||
<h2 className='text-2xl font-bold mt-4'>깃허브 계정 연동</h2>
|
||||
<p className='text-gray-400 mb-2'>연동한 깃허브 계정은 프로필에 표시됩니다.</p>
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { NextPage, NextPageContext } from 'next'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Link from 'next/link'
|
||||
import { NextSeo } from 'next-seo'
|
||||
|
||||
import { get } from '@utils/Query'
|
||||
import { git } from '@utils/Constants'
|
||||
import Day from '@utils/Day'
|
||||
|
||||
import { SubmittedBot, User } from '@types'
|
||||
|
||||
@ -11,9 +13,8 @@ import useCopyClipboard from 'react-use-clipboard'
|
||||
import { ParsedUrlQuery } from 'querystring'
|
||||
|
||||
import NotFound from 'pages/404'
|
||||
import Day from '@utils/Day'
|
||||
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const Divider = dynamic(() => import('@components/Divider'))
|
||||
const LongButton = dynamic(() => import('@components/LongButton'))
|
||||
const Tag = dynamic(() => import('@components/Tag'))
|
||||
@ -29,7 +30,7 @@ const PendingBot: NextPage<PendingBotProps> = ({ data }) => {
|
||||
})
|
||||
if(!data) return <NotFound />
|
||||
return <Container paddingTop className='py-10'>
|
||||
<SEO title='심사이력' />
|
||||
<NextSeo title='심사이력' />
|
||||
<div className='lg:flex w-full'>
|
||||
<div className='w-full lg:w-3/4 lg:pr-5 py-8 text-center lg:text-left'>
|
||||
{
|
||||
|
||||
@ -9,7 +9,6 @@ import { SearchQuerySchema } from '@utils/Yup'
|
||||
|
||||
const Hero = dynamic(() => import('@components/Hero'))
|
||||
const Advertisement = dynamic(() => import('@components/Advertisement'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const BotCard = dynamic(() => import('@components/BotCard'))
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
const ResponsiveGrid = dynamic(() => import('@components/ResponsiveGrid'))
|
||||
@ -20,7 +19,6 @@ const Search:NextPage<SearchProps> = ({ data, query }) => {
|
||||
if(!query?.q) return <Redirect text={false} to='/' />
|
||||
return <>
|
||||
<Hero header={`"${query.q}" 검색 결과`} description={`'${query.q}' 에 대한 검색 결과입니다.`} />
|
||||
<SEO title={`"${query.q}" 검색 결과`} description={`'${query.q}' 에 대한 검색 결과입니다.`} />
|
||||
<section id='list'>
|
||||
<Container>
|
||||
<Advertisement />
|
||||
|
||||
@ -15,9 +15,9 @@ import { ReportSchema } from '@utils/Yup'
|
||||
|
||||
import NotFound from '../404'
|
||||
import { KoreanbotsEndPoints, reportCats } from '@utils/Constants'
|
||||
import { NextSeo } from 'next-seo'
|
||||
|
||||
const Container = dynamic(() => import('@components/Container'))
|
||||
const SEO = dynamic(() => import('@components/SEO'))
|
||||
const DiscordAvatar = dynamic(() => import('@components/DiscordAvatar'))
|
||||
const Divider = dynamic(() => import('@components/Divider'))
|
||||
const BotCard = dynamic(() => import('@components/BotCard'))
|
||||
@ -37,7 +37,7 @@ const Users: NextPage<UserProps> = ({ user, data, csrfToken, theme }) => {
|
||||
if (!data?.id) return <NotFound />
|
||||
return (
|
||||
<Container paddingTop className='py-10'>
|
||||
<SEO
|
||||
<NextSeo
|
||||
title={data.username}
|
||||
description={data.bots.length === 0 ? `${data.username}님의 프로필입니다.` : josa(
|
||||
`${(data.bots as Bot[])
|
||||
@ -45,7 +45,14 @@ const Users: NextPage<UserProps> = ({ user, data, csrfToken, theme }) => {
|
||||
.map(el => el.name)
|
||||
.join(', ')}#{을} 제작합니다.`
|
||||
)}
|
||||
image={KoreanbotsEndPoints.CDN.avatar(data.id, { format: 'png', size: 256 })}
|
||||
openGraph={{
|
||||
images: [{
|
||||
url: KoreanbotsEndPoints.CDN.avatar(data.id, { format: 'png', size: 256 }),
|
||||
width: 256,
|
||||
height: 256,
|
||||
alt: 'User Avatar'
|
||||
}]
|
||||
}}
|
||||
/>
|
||||
<div className='lg:flex'>
|
||||
<div className='w-full text-center lg:w-1/4'>
|
||||
|
||||
@ -81,7 +81,7 @@ if (!self.define) {
|
||||
});
|
||||
};
|
||||
}
|
||||
define("./sw.js",['./workbox-6b19f60b'], function (workbox) { 'use strict';
|
||||
define("./sw.js",['./workbox'], function (workbox) { 'use strict';
|
||||
|
||||
/**
|
||||
* Welcome to your Workbox-powered service worker!
|
||||
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"sw.js","sources":["../../../../private/var/folders/s2/c5ly4ybd1k92c69fqc4_50rm0000gn/T/bbfed4b2337555e56e35b242711c8b06/sw.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from '/Users/wonderlandpark/Workspace/koreanbots/node_modules/workbox-routing/registerRoute.mjs';\nimport {NetworkFirst as workbox_strategies_NetworkFirst} from '/Users/wonderlandpark/Workspace/koreanbots/node_modules/workbox-strategies/NetworkFirst.mjs';\nimport {NetworkOnly as workbox_strategies_NetworkOnly} from '/Users/wonderlandpark/Workspace/koreanbots/node_modules/workbox-strategies/NetworkOnly.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from '/Users/wonderlandpark/Workspace/koreanbots/node_modules/workbox-core/clientsClaim.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\nimportScripts(\n \"fallback-development.js\"\n);\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n\nworkbox_routing_registerRoute(\"/\", new workbox_strategies_NetworkFirst({ \"cacheName\":\"start-url\", plugins: [{ cacheWillUpdate: async ({request, response, event, state}) => { if (response && response.type === 'opaqueredirect') { return new Response(response.body, {status: 200, statusText: 'OK', headers: response.headers}); } return response; } }, { handlerDidError: async ({request}) => self.fallback(request) }] }), 'GET');\nworkbox_routing_registerRoute(/.*/i, new workbox_strategies_NetworkOnly({ \"cacheName\":\"dev\", plugins: [{ handlerDidError: async ({request}) => self.fallback(request) }] }), 'GET');\n\n\n\n\n"],"names":["importScripts","self","skipWaiting","workbox_core_clientsClaim","workbox_routing_registerRoute","workbox_strategies_NetworkFirst","plugins","cacheWillUpdate","request","response","event","state","type","Response","body","status","statusText","headers","handlerDidError","fallback","workbox_strategies_NetworkOnly"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGgJ;EAChJ;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAGAA,aAAa,CACX,yBADW,CAAb;EAUAC,IAAI,CAACC,WAAL;AAEAC,sBAAyB;AAIzBC,uBAA6B,CAAC,GAAD,EAAM,IAAIC,oBAAJ,CAAoC;EAAE,eAAY,WAAd;EAA2BC,EAAAA,OAAO,EAAE,CAAC;EAAEC,IAAAA,eAAe,EAAE,OAAO;EAACC,MAAAA,OAAD;EAAUC,MAAAA,QAAV;EAAoBC,MAAAA,KAApB;EAA2BC,MAAAA;EAA3B,KAAP,KAA6C;EAAE,UAAIF,QAAQ,IAAIA,QAAQ,CAACG,IAAT,KAAkB,gBAAlC,EAAoD;EAAE,eAAO,IAAIC,QAAJ,CAAaJ,QAAQ,CAACK,IAAtB,EAA4B;EAACC,UAAAA,MAAM,EAAE,GAAT;EAAcC,UAAAA,UAAU,EAAE,IAA1B;EAAgCC,UAAAA,OAAO,EAAER,QAAQ,CAACQ;EAAlD,SAA5B,CAAP;EAAiG;;EAAC,aAAOR,QAAP;EAAkB;EAA5O,GAAD,EAAiP;EAAES,IAAAA,eAAe,EAAE,OAAO;EAACV,MAAAA;EAAD,KAAP,KAAqBP,IAAI,CAACkB,QAAL,CAAcX,OAAd;EAAxC,GAAjP;EAApC,CAApC,CAAN,EAAqY,KAArY,CAA7B;AACAJ,uBAA6B,CAAC,KAAD,EAAQ,IAAIgB,mBAAJ,CAAmC;EAAE,eAAY,KAAd;EAAqBd,EAAAA,OAAO,EAAE,CAAC;EAAEY,IAAAA,eAAe,EAAE,OAAO;EAACV,MAAAA;EAAD,KAAP,KAAqBP,IAAI,CAACkB,QAAL,CAAcX,OAAd;EAAxC,GAAD;EAA9B,CAAnC,CAAR,EAAgJ,KAAhJ,CAA7B;;"}
|
||||
File diff suppressed because one or more lines are too long
@ -1,4 +1,4 @@
|
||||
define("./workbox-6b19f60b.js",['exports'], function (exports) { 'use strict';
|
||||
define("./workbox.js",['exports'], function (exports) { 'use strict';
|
||||
|
||||
try {
|
||||
self['workbox:core:6.1.5'] && _();
|
||||
@ -2698,4 +2698,3 @@ define("./workbox-6b19f60b.js",['exports'], function (exports) { 'use strict';
|
||||
exports.registerRoute = registerRoute;
|
||||
|
||||
});
|
||||
//# sourceMappingURL=workbox-6b19f60b.js.map
|
||||
@ -190,10 +190,11 @@ export const DiscordEnpoints = {
|
||||
export const KoreanbotsEndPoints = {
|
||||
OG: class {
|
||||
static root = 'https://og.kbots.link'
|
||||
static origin = 'https://beta.koreanbots.dev'
|
||||
static bot(id: string, name: string, bio: string, tags: string[], stats: string[]) {
|
||||
const u = new URL(this.root)
|
||||
u.pathname = name
|
||||
u.searchParams.append('image', KoreanbotsEndPoints.CDN.avatar(id, { format: 'webp', size: 256 }))
|
||||
u.searchParams.append('image', this.origin + KoreanbotsEndPoints.CDN.avatar(id, { format: 'webp', size: 256 }))
|
||||
u.searchParams.append('bio', bio)
|
||||
tags.map(t => u.searchParams.append('tags', t))
|
||||
stats.map(s => u.searchParams.append('stats', s))
|
||||
|
||||
@ -6579,6 +6579,11 @@ next-pwa@5.2.21:
|
||||
workbox-webpack-plugin "^6.1.5"
|
||||
workbox-window "^6.1.5"
|
||||
|
||||
next-seo@4.24.0:
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-4.24.0.tgz#7c0447da00b8574dcda5c6979771a7f6efd24f55"
|
||||
integrity sha512-9VQXfXAelhE+hAWzJ4azigQaW3FPX0kU0eYKFQXKsQjgY7AWtukjRGXls0oSIk8khhDJwmCt46EwsO9n5DDW6Q==
|
||||
|
||||
next-session@3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/next-session/-/next-session-3.4.0.tgz#799757624a336b752298e9771ffc1d7282d76e91"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user