mirror of
https://github.com/koreanbots/core.git
synced 2025-12-15 14:10:22 +00:00
feat: added github
This commit is contained in:
parent
85071cac6d
commit
0b0d5e4dba
48
pages/api/auth/github/callback.ts
Normal file
48
pages/api/auth/github/callback.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { NextApiRequest } from 'next'
|
||||||
|
import fetch from 'node-fetch'
|
||||||
|
|
||||||
|
import { GithubTokenInfo } from '@types'
|
||||||
|
import { SpecialEndPoints } from '@utils/Constants'
|
||||||
|
import { OauthCallbackSchema } from '@utils/Yup'
|
||||||
|
import ResponseWrapper from '@utils/ResponseWrapper'
|
||||||
|
import { get, update } from '@utils/Query'
|
||||||
|
import RequestHandler from '@utils/RequestHandler'
|
||||||
|
|
||||||
|
const Callback = RequestHandler().get(async (req: ApiRequest, res) => {
|
||||||
|
const validate = await OauthCallbackSchema.validate(req.query)
|
||||||
|
.then(r => r)
|
||||||
|
.catch(e => {
|
||||||
|
ResponseWrapper(res, { code: 400, errors: e.errors })
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!validate) return
|
||||||
|
|
||||||
|
const user = await get.Authorization(req.cookies.token)
|
||||||
|
if (!user) return ResponseWrapper(res, { code: 401 })
|
||||||
|
const token: GithubTokenInfo = await fetch(SpecialEndPoints.Github.Token(process.env.GITHUB_CLIENT_ID, process.env.GITHUB_CLIENT_SECRET,req.query.code), {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json'
|
||||||
|
},
|
||||||
|
}).then(r => r.json())
|
||||||
|
if (token.error) return ResponseWrapper(res, { code: 400, errors: ['올바르지 않은 코드입니다.'] })
|
||||||
|
|
||||||
|
const github: { login: string } = await fetch(SpecialEndPoints.Github.Me, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `token ${token.access_token}`
|
||||||
|
}
|
||||||
|
}).then(r => r.json())
|
||||||
|
const result = await update.Github(user, github.login)
|
||||||
|
if(result === 0) return ResponseWrapper(res, { code: 400, message: '이미 등록되어있는 깃허브 계정입니다.' })
|
||||||
|
get.user.clear(user)
|
||||||
|
res.redirect(301, '/panel')
|
||||||
|
})
|
||||||
|
|
||||||
|
interface ApiRequest extends NextApiRequest {
|
||||||
|
query: {
|
||||||
|
code: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Callback
|
||||||
30
pages/api/auth/github/index.ts
Normal file
30
pages/api/auth/github/index.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
import { generateOauthURL } from '@utils/Tools'
|
||||||
|
import RequestHandler from '@utils/RequestHandler'
|
||||||
|
import ResponseWrapper from '@utils/ResponseWrapper'
|
||||||
|
import { get, update } from '@utils/Query'
|
||||||
|
import { checkToken } from '@utils/Csrf'
|
||||||
|
|
||||||
|
const Github = RequestHandler().get(async (_req: NextApiRequest, res: NextApiResponse) => {
|
||||||
|
res.redirect(
|
||||||
|
301,
|
||||||
|
generateOauthURL('github', process.env.GITHUB_CLIENT_ID)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.delete(async (req: DeleteApiRequest, res) => {
|
||||||
|
const user = await get.Authorization(req.cookies.token)
|
||||||
|
if (!user) return ResponseWrapper(res, { code: 401 })
|
||||||
|
const csrfValidated = checkToken(req, res, req.body._csrf)
|
||||||
|
if(!csrfValidated) return
|
||||||
|
await update.Github(user, null)
|
||||||
|
get.user.clear(user)
|
||||||
|
return ResponseWrapper(res, { code: 200 })
|
||||||
|
})
|
||||||
|
|
||||||
|
interface DeleteApiRequest extends NextApiRequest {
|
||||||
|
body: {
|
||||||
|
_csrf: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Github
|
||||||
@ -6,15 +6,17 @@ import { useRouter } from 'next/router'
|
|||||||
import { get } from '@utils/Query'
|
import { get } from '@utils/Query'
|
||||||
import { parseCookie, redirectTo } from '@utils/Tools'
|
import { parseCookie, redirectTo } from '@utils/Tools'
|
||||||
import { Bot, SubmittedBot, User } from '@types'
|
import { Bot, SubmittedBot, User } from '@types'
|
||||||
import BotCard from '@components/BotCard'
|
import Fetch from '@utils/Fetch'
|
||||||
import SubmittedBotCard from '@components/SubmittedBotCard'
|
import { getToken } from '@utils/Csrf'
|
||||||
import Button from '@components/Button'
|
|
||||||
|
|
||||||
const Container = dynamic(() => import('@components/Container'))
|
const Container = dynamic(() => import('@components/Container'))
|
||||||
const SEO = dynamic(() => import('@components/SEO'))
|
const SEO = dynamic(() => import('@components/SEO'))
|
||||||
const ResponsiveGrid = dynamic(() => import('@components/ResponsiveGrid'))
|
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 Panel:NextPage<PanelProps> = ({ logged, user, submits }) => {
|
const Panel:NextPage<PanelProps> = ({ logged, user, submits, csrfToken }) => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [ submitLimit, setSubmitLimit ] = useState(8)
|
const [ submitLimit, setSubmitLimit ] = useState(8)
|
||||||
function toLogin() {
|
function toLogin() {
|
||||||
@ -28,6 +30,18 @@ const Panel:NextPage<PanelProps> = ({ logged, user, submits }) => {
|
|||||||
return <Container paddingTop className='pt-5 pb-10'>
|
return <Container paddingTop className='pt-5 pb-10'>
|
||||||
<SEO title='관리 패널' />
|
<SEO title='관리 패널' />
|
||||||
<h1 className='text-4xl font-bold'>관리 패널</h1>
|
<h1 className='text-4xl font-bold'>관리 패널</h1>
|
||||||
|
<h2 className='text-3xl font-bold my-4'>깃허브 계정 연동</h2>
|
||||||
|
<Button className='bg-github-black hover:opacity-80' onClick={user.github ? async () => {
|
||||||
|
await Fetch('/api/auth/github', {
|
||||||
|
method: 'DELETE',
|
||||||
|
body: JSON.stringify({
|
||||||
|
_csrf: csrfToken
|
||||||
|
})
|
||||||
|
}, true)
|
||||||
|
router.reload()
|
||||||
|
} : null} href={user.github ? null : '/api/auth/github'}>
|
||||||
|
<i className='fab fa-github' /> 깃허브 계정 연동 {user.github ? '취소' : ''}
|
||||||
|
</Button>
|
||||||
<div className='mt-6'>
|
<div className='mt-6'>
|
||||||
<h2 className='text-3xl font-bold'>나의 봇</h2>
|
<h2 className='text-3xl font-bold'>나의 봇</h2>
|
||||||
{
|
{
|
||||||
@ -68,13 +82,14 @@ export const getServerSideProps = async (ctx: NextPageContext) => {
|
|||||||
const user = await get.Authorization(parsed?.token) || ''
|
const user = await get.Authorization(parsed?.token) || ''
|
||||||
const submits = await get.botSubmits.load(user)
|
const submits = await get.botSubmits.load(user)
|
||||||
|
|
||||||
return { props: { logged: !!user, user: await get.user.load(user), submits } }
|
return { props: { logged: !!user, user: await get.user.load(user), submits, csrfToken: getToken(ctx.req, ctx.res) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PanelProps {
|
interface PanelProps {
|
||||||
logged: boolean
|
logged: boolean
|
||||||
user: User
|
user: User
|
||||||
submits: SubmittedBot[]
|
submits: SubmittedBot[]
|
||||||
|
csrfToken: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Panel
|
export default Panel
|
||||||
@ -26,7 +26,8 @@ module.exports = {
|
|||||||
'discord-dark-hover': '#383f48',
|
'discord-dark-hover': '#383f48',
|
||||||
'discord-black': '#23272A',
|
'discord-black': '#23272A',
|
||||||
'discord-pink': '#FF73FA',
|
'discord-pink': '#FF73FA',
|
||||||
'very-black': '#1b1e23'
|
'very-black': '#1b1e23',
|
||||||
|
'github-black': '#24292e'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
minHeight: {
|
minHeight: {
|
||||||
|
|||||||
@ -115,6 +115,13 @@ export interface DiscordTokenInfo {
|
|||||||
error?: string
|
error?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GithubTokenInfo {
|
||||||
|
access_token?: string
|
||||||
|
scope?: string
|
||||||
|
token_type?: string
|
||||||
|
error?: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface TokenRegister {
|
export interface TokenRegister {
|
||||||
id: string
|
id: string
|
||||||
access_token: string
|
access_token: string
|
||||||
|
|||||||
@ -180,12 +180,20 @@ export const KoreanbotsEndPoints = {
|
|||||||
logout: '/api/auth/discord/logout'
|
logout: '/api/auth/discord/logout'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SpecialEndPoints = {
|
||||||
|
Github: {
|
||||||
|
Token: (clientID: string, clientSecret: string, code: string) => `https://github.com/login/oauth/access_token?client_id=${clientID}&client_secret=${clientSecret}&code=${code}`,
|
||||||
|
Me: 'https://api.github.com/user'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const GlobalRatelimitIgnore = [
|
export const GlobalRatelimitIgnore = [
|
||||||
'/api/image/discord/avatars/'
|
'/api/image/discord/avatars/'
|
||||||
]
|
]
|
||||||
|
|
||||||
export const Oauth = {
|
export const Oauth = {
|
||||||
discord: (clientID: string, scope: string) => `https://discord.com/oauth2/authorize?client_id=${clientID}&scope=${scope}&permissions=0&response_type=code&redirect_uri=${process.env.KOREANBOTS_URL}/api/auth/discord/callback&prompt=none`
|
discord: (clientID: string, scope: string) => `https://discord.com/oauth2/authorize?client_id=${clientID}&scope=${scope}&permissions=0&response_type=code&redirect_uri=${process.env.KOREANBOTS_URL}/api/auth/discord/callback&prompt=none`,
|
||||||
|
github: (clientID: string) => `https://github.com/login/oauth/authorize?client_id=${clientID}&redirect_uri=${process.env.KOREANBOTS_URL}/api/auth/github/callback`
|
||||||
}
|
}
|
||||||
export const git = { 'github.com': { icon: 'github', text: 'Github' }, 'gitlab.com': { icon: 'gitlab', text: 'Gitlab' }}
|
export const git = { 'github.com': { icon: 'github', text: 'Github' }, 'gitlab.com': { icon: 'gitlab', text: 'Gitlab' }}
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { ResponseProps } from '@types'
|
import { ResponseProps } from '@types'
|
||||||
import { KoreanbotsEndPoints } from './Constants'
|
import { KoreanbotsEndPoints } from './Constants'
|
||||||
|
|
||||||
const Fetch = async <T>(endpoint: string, options?: RequestInit): Promise<ResponseProps<T>> => {
|
const Fetch = async <T>(endpoint: string, options?: RequestInit, rawEndpoint=false): Promise<ResponseProps<T>> => {
|
||||||
const url = KoreanbotsEndPoints.baseAPI + (endpoint.startsWith('/') ? endpoint : '/' + endpoint)
|
const url = (rawEndpoint ? '' : KoreanbotsEndPoints.baseAPI) + (endpoint.startsWith('/') ? endpoint : '/' + endpoint)
|
||||||
|
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
|||||||
@ -250,6 +250,13 @@ async function resetBotToken(id: string, beforeToken: string) {
|
|||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function Github(id: string, github: string) {
|
||||||
|
const user = await knex('users').where({ github }).whereNot({ id })
|
||||||
|
if(github && user.length !== 0) return 0
|
||||||
|
await knex('users').update({ github }).where({ id })
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
async function getImage(url: string) {
|
async function getImage(url: string) {
|
||||||
const res = await fetch(url)
|
const res = await fetch(url)
|
||||||
if(!res.ok) return null
|
if(!res.ok) return null
|
||||||
@ -373,7 +380,8 @@ export const update = {
|
|||||||
assignToken,
|
assignToken,
|
||||||
updateBotApplication,
|
updateBotApplication,
|
||||||
resetBotToken,
|
resetBotToken,
|
||||||
updateServer
|
updateServer,
|
||||||
|
Github
|
||||||
}
|
}
|
||||||
|
|
||||||
export const put = {
|
export const put = {
|
||||||
|
|||||||
@ -75,7 +75,7 @@ export function checkBrowser(): string {
|
|||||||
return M.join(' ')
|
return M.join(' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateOauthURL(provider: 'discord', clientID: string, scope: string) {
|
export function generateOauthURL(provider: 'discord'|'github', clientID: string, scope?: string) {
|
||||||
return Oauth[provider](clientID, scope)
|
return Oauth[provider](clientID, scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user