fix: router instance called at serverside while rendering

This commit is contained in:
wonderlandpark 2021-05-06 09:14:48 +09:00
parent 7ba2096c31
commit dcdd7e0710
No known key found for this signature in database
GPG Key ID: E3E650B146478C64
10 changed files with 60 additions and 61 deletions

16
components/Login.tsx Normal file
View File

@ -0,0 +1,16 @@
import { redirectTo } from '@utils/Tools'
import { useRouter } from 'next/router'
import { useEffect } from 'react'
const Login: React.FC = ({ children }) => {
const router = useRouter()
useEffect(() => {
localStorage.redirectTo = window.location.href
redirectTo(router, 'login')
})
return <>
{children}
</>
}
export default Login

View File

@ -1,4 +1,4 @@
import { ReactNode } from 'react'
import { ReactNode, useEffect } from 'react'
import dynamic from 'next/dynamic'
import { useRouter } from 'next/router'
@ -6,22 +6,25 @@ import { redirectTo } from '@utils/Tools'
const Container = dynamic(() => import('@components/Container'))
const Redirect = ({ to, children }:RedirectProps):JSX.Element => {
const Redirect = ({ to, text=true, children }:RedirectProps):JSX.Element => {
const router = useRouter()
if(!to) throw new Error('No Link')
redirectTo(router, to)
useEffect(() => {
redirectTo(router, to)
}, [])
if(children) return <>
{children}
</>
return <Container paddingTop>
<div>
<a href={to} className='text-blue-400'> .</a>
<a href={to} className='text-blue-400'>{text && '자동으로 리다이렉트되지 않는다면 클릭해세요.'}</a>
</div>
</Container>
}
interface RedirectProps {
to: string
text?: boolean
children?: ReactNode
}

View File

@ -35,6 +35,7 @@ const Button = dynamic(() => import('@components/Button'))
const TextArea = dynamic(() => import('@components/Form/TextArea'))
const Modal = dynamic(() => import('@components/Modal'))
const NSFW = dynamic(() => import('@components/NSFW'))
const Login = dynamic(() => import('@components/Login'))
const Bots: NextPage<BotsProps> = ({ data, desc, date, user, theme, csrfToken }) => {
const bg = checkBotFlag(data?.flags, 'trusted') && data?.banner
@ -42,10 +43,6 @@ const Bots: NextPage<BotsProps> = ({ data, desc, date, user, theme, csrfToken })
const [ nsfw, setNSFW ] = useState<boolean>()
const [ reportModal, setReportModal ] = useState(false)
const [ reportRes, setReportRes ] = useState<ResponseProps<null>>(null)
function toLogin() {
localStorage.redirectTo = window.location.href
redirectTo(router, 'login')
}
useEffect(() => {
setNSFW(localStorage.nsfw)
}, [])
@ -188,7 +185,7 @@ const Bots: NextPage<BotsProps> = ({ data, desc, date, user, theme, csrfToken })
))}
<div className='list grid'>
<a className='text-red-600 hover:underline cursor-pointer' onClick={() => {
if(!user) toLogin()
if(!user) return <Login />
else setReportModal(true)
}} aria-hidden='true'>
<i className='far fa-flag' />

View File

@ -5,7 +5,7 @@ import { useRouter } from 'next/router'
import { Bot, CsrfContext, ResponseProps, Theme, User } from '@types'
import { get } from '@utils/Query'
import { makeBotURL, parseCookie, checkBotFlag, redirectTo } from '@utils/Tools'
import { makeBotURL, parseCookie, checkBotFlag } from '@utils/Tools'
import { ParsedUrlQuery } from 'querystring'
@ -16,6 +16,7 @@ import { useState } from 'react'
import Fetch from '@utils/Fetch'
import Day from '@utils/Day'
import { getJosaPicker } from 'josa'
import { KoreanbotsEndPoints } from '@utils/Constants'
const Container = dynamic(() => import('@components/Container'))
@ -25,6 +26,7 @@ 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 [ votingStatus, setVotingStatus ] = useState(0)
@ -35,18 +37,13 @@ const VoteBot: NextPage<VoteBotProps> = ({ data, user, csrfToken, theme }) => {
router.push(`/bots/${data.id}`)
return <></>
}
if(!user) {
localStorage.redirectTo = window.location.href
redirectTo(router, 'login')
return <SEO title='새로운 봇 추가하기' description='자신의 봇을 한국 디스코드봇 리스트에 등록하세요.'/>
}
if(!user) return <Login>
<SEO title={data.name} description={`한국 디스코드봇 리스트에서 ${data.name}에 투표하세요`} image={KoreanbotsEndPoints.CDN.avatar(data.id, { format: 'png', size: 256 })} />
</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={
data.avatar
? `https://cdn.discordapp.com/avatars/${data.id}/${data.avatar}.png?size=1024`
: `https://cdn.discordapp.com/embed/avatars/${Number(data.tag) % 5}.png?size=1024`
} />
<SEO title={data.name} description={`한국 디스코드봇 리스트에서 ${data.name}에 투표하세요`} image={KoreanbotsEndPoints.CDN.avatar(data.id, { format: 'png', size: 256 })} />
<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>

View File

@ -1,22 +1,20 @@
import { NextPage, NextPageContext } from 'next'
import dynamic from 'next/dynamic'
import { useRouter } from 'next/router'
import { get } from '@utils/Query'
import { parseCookie, redirectTo } from '@utils/Tools'
import { parseCookie } from '@utils/Tools'
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 }) => {
const router = useRouter()
if(!user) {
localStorage.redirectTo = window.location.href
redirectTo(router, 'login')
return
}
if(!user) return <Login>
<SEO title='한디리 개발자' description='한국 디스코드봇 리스트 API를 활용하여 봇에 다양한 기능을 추가해보세요.' />
</Login>
return <DeveloperLayout enabled='applications'>
<h1 className='text-3xl font-bold'> </h1>
<p className='text-gray-400'> API를 .</p>

View File

@ -35,6 +35,7 @@ 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 }) => {
const [ data, setData ] = useState(null)
@ -42,10 +43,6 @@ const ManageBotPage:NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme })
const [ transferModal, setTransferModal ] = useState(false)
const [ deleteModal, setDeleteModal ] = useState(false)
const router = useRouter()
function toLogin() {
localStorage.redirectTo = window.location.href
redirectTo(router, 'login')
}
async function submitBot(value: ManageBot) {
const res = await Fetch(`/bots/${bot.id}`, { method: 'PATCH', body: JSON.stringify(cleanObject<ManageBot>(value)) })
@ -59,10 +56,9 @@ const ManageBotPage:NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme })
}
if(!bot) return <NotFound />
if(!user) {
toLogin()
return <SEO title='봇 정보 수정하기' description='봇의 정보를 수정합니다.'/>
}
if(!user) return <Login>
<SEO 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='봇의 정보를 수정합니다.'/>

View File

@ -4,7 +4,7 @@ import dynamic from 'next/dynamic'
import { useRouter } from 'next/router'
import { get } from '@utils/Query'
import { parseCookie, redirectTo } from '@utils/Tools'
import { parseCookie } from '@utils/Tools'
import { Bot, SubmittedBot, User } from '@types'
import Fetch from '@utils/Fetch'
import { getToken } from '@utils/Csrf'
@ -15,18 +15,14 @@ const ResponsiveGrid = dynamic(() => import('@components/ResponsiveGrid'))
const Button = dynamic(() => import('@components/Button'))
const BotCard = dynamic(() => import('@components/BotCard'))
const SubmittedBotCard = dynamic(() => import('@components/SubmittedBotCard'))
const Login = dynamic(() => import('@components/Login'))
const Panel:NextPage<PanelProps> = ({ logged, user, submits, csrfToken }) => {
const router = useRouter()
const [ submitLimit, setSubmitLimit ] = useState(8)
function toLogin() {
localStorage.redirectTo = window.location.href
redirectTo(router, 'login')
}
if(!logged) {
toLogin()
return <SEO title='관리 패널' />
}
if(!logged) return <Login>
<SEO title='관리 패널' />
</Login>
return <Container paddingTop className='pt-5 pb-10'>
<SEO title='관리 패널' />
<h1 className='text-4xl font-bold'> </h1>

View File

@ -1,12 +1,10 @@
import { NextPage, NextPageContext } from 'next'
import dynamic from 'next/dynamic'
import { useRouter } from 'next/router'
import { ParsedUrlQuery } from 'querystring'
import { BotList } from '@types'
import { get } from '@utils/Query'
import { SearchQuerySchema } from '@utils/Yup'
import { redirectTo } from '@utils/Tools'
const Hero = dynamic(() => import('@components/Hero'))
@ -16,13 +14,10 @@ const BotCard = dynamic(() => import('@components/BotCard'))
const Container = dynamic(() => import('@components/Container'))
const ResponsiveGrid = dynamic(() => import('@components/ResponsiveGrid'))
const Paginator = dynamic(() => import('@components/Paginator'))
const Redirect = dynamic(() => import('@components/Redirect'))
const Search:NextPage<SearchProps> = ({ data, query }) => {
const router = useRouter()
if(!query?.q) {
redirectTo(router, '/')
return <></>
}
if(!query?.q) return <Redirect text={false} to='/' />
return <>
<Hero header={`"${query.q}" 검색 결과`} description={`'${query.q}' 에 대한 검색 결과입니다.`} />
<SEO title={`"${query.q}" 검색 결과`} description={`'${query.q}' 에 대한 검색 결과입니다.`} />
@ -46,6 +41,11 @@ const Search:NextPage<SearchProps> = ({ data, query }) => {
}
export const getServerSideProps = async(ctx: Context) => {
if(!ctx.query?.q) {
ctx.res.statusCode = 301
ctx.res.setHeader('Location', '/')
return { props: {} }
}
let data: BotList
if(!ctx.query.page) ctx.query.page = '1'
const validate = await SearchQuerySchema.validate(ctx.query).then(el => el).catch(() => null)

View File

@ -1,6 +1,5 @@
import { NextPage, NextPageContext } from 'next'
import { useState } from 'react'
import { useRouter } from 'next/router'
import dynamic from 'next/dynamic'
import { SnowflakeUtil } from 'discord.js'
import { ParsedUrlQuery } from 'querystring'
@ -9,7 +8,7 @@ import { Field, Form, Formik } from 'formik'
import { Bot, User, ResponseProps, Theme } from '@types'
import { get } from '@utils/Query'
import { checkUserFlag, parseCookie, redirectTo } from '@utils/Tools'
import { checkUserFlag, parseCookie } from '@utils/Tools'
import { getToken } from '@utils/Csrf'
import Fetch from '@utils/Fetch'
import { ReportSchema } from '@utils/Yup'
@ -30,15 +29,11 @@ const Message = dynamic(() => import('@components/Message'))
const Modal = dynamic(() => import('@components/Modal'))
const Button = dynamic(() => import('@components/Button'))
const TextArea = dynamic(() => import('@components/Form/TextArea'))
const Login = dynamic(() => import('@components/Login'))
const Users: NextPage<UserProps> = ({ user, data, csrfToken, theme }) => {
const router = useRouter()
const [ reportModal, setReportModal ] = useState(false)
const [ reportRes, setReportRes ] = useState<ResponseProps<null>>(null)
function toLogin() {
localStorage.redirectTo = window.location.href
redirectTo(router, 'login')
}
if (!data?.id) return <NotFound />
return (
<Container paddingTop className='py-10'>
@ -92,7 +87,7 @@ const Users: NextPage<UserProps> = ({ user, data, csrfToken, theme }) => {
)}
<div className='list-none mt-2'>
<a className='text-red-600 hover:underline cursor-pointer' onClick={() => {
if(!user) toLogin()
if(!user) return <Login />
else setReportModal(true)
}} aria-hidden='true'>
<i className='far fa-flag' />

View File

@ -103,7 +103,8 @@ export function bufferToStream(binary: Buffer) {
return readableInstanceStream
}
export function parseCookie(req: { headers: { cookie?: string }}): { [key: string]: string } {
export function parseCookie(req?: { headers: { cookie?: string }}): { [key: string]: string } {
if(!req) return {}
return cookie.parse(req.headers.cookie || '')
}