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 { parseCookie, redirectTo } from '@utils/Tools'
|
||||
import { Bot, SubmittedBot, User } from '@types'
|
||||
import BotCard from '@components/BotCard'
|
||||
import SubmittedBotCard from '@components/SubmittedBotCard'
|
||||
import Button from '@components/Button'
|
||||
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'))
|
||||
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 [ submitLimit, setSubmitLimit ] = useState(8)
|
||||
function toLogin() {
|
||||
@ -28,6 +30,18 @@ const Panel:NextPage<PanelProps> = ({ logged, user, submits }) => {
|
||||
return <Container paddingTop className='pt-5 pb-10'>
|
||||
<SEO title='관리 패널' />
|
||||
<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'>
|
||||
<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 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 {
|
||||
logged: boolean
|
||||
user: User
|
||||
submits: SubmittedBot[]
|
||||
csrfToken: string
|
||||
}
|
||||
|
||||
export default Panel
|
||||
@ -26,7 +26,8 @@ module.exports = {
|
||||
'discord-dark-hover': '#383f48',
|
||||
'discord-black': '#23272A',
|
||||
'discord-pink': '#FF73FA',
|
||||
'very-black': '#1b1e23'
|
||||
'very-black': '#1b1e23',
|
||||
'github-black': '#24292e'
|
||||
}
|
||||
},
|
||||
minHeight: {
|
||||
|
||||
@ -115,6 +115,13 @@ export interface DiscordTokenInfo {
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface GithubTokenInfo {
|
||||
access_token?: string
|
||||
scope?: string
|
||||
token_type?: string
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface TokenRegister {
|
||||
id: string
|
||||
access_token: string
|
||||
|
||||
@ -180,12 +180,20 @@ export const KoreanbotsEndPoints = {
|
||||
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 = [
|
||||
'/api/image/discord/avatars/'
|
||||
]
|
||||
|
||||
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' }}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { ResponseProps } from '@types'
|
||||
import { KoreanbotsEndPoints } from './Constants'
|
||||
|
||||
const Fetch = async <T>(endpoint: string, options?: RequestInit): Promise<ResponseProps<T>> => {
|
||||
const url = KoreanbotsEndPoints.baseAPI + (endpoint.startsWith('/') ? endpoint : '/' + endpoint)
|
||||
const Fetch = async <T>(endpoint: string, options?: RequestInit, rawEndpoint=false): Promise<ResponseProps<T>> => {
|
||||
const url = (rawEndpoint ? '' : KoreanbotsEndPoints.baseAPI) + (endpoint.startsWith('/') ? endpoint : '/' + endpoint)
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: 'GET',
|
||||
|
||||
@ -250,6 +250,13 @@ async function resetBotToken(id: string, beforeToken: string) {
|
||||
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) {
|
||||
const res = await fetch(url)
|
||||
if(!res.ok) return null
|
||||
@ -373,7 +380,8 @@ export const update = {
|
||||
assignToken,
|
||||
updateBotApplication,
|
||||
resetBotToken,
|
||||
updateServer
|
||||
updateServer,
|
||||
Github
|
||||
}
|
||||
|
||||
export const put = {
|
||||
|
||||
@ -75,7 +75,7 @@ export function checkBrowser(): string {
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user