Skip to content

Commit

Permalink
feat(web): 完成授权登录流程
Browse files Browse the repository at this point in the history
  • Loading branch information
xinyao27 committed Jun 13, 2022
1 parent e248a90 commit d26286d
Show file tree
Hide file tree
Showing 22 changed files with 661 additions and 51 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"@ohbug/react": "^2.0.3",
"@ohbug/types": "^2.0.3",
"@ohbug/vue": "^2.0.3",
"@prisma/client": "^3.11.1",
"@prisma/client": "^3.15.1",
"dayjs": "^1.11.2",
"dotenv": "^16.0.1",
"ramda": "^0.28.0",
Expand All @@ -59,7 +59,7 @@
"esno": "^0.16.3",
"husky": "^8.0.1",
"lint-staged": "^12.5.0",
"prisma": "^3.11.1",
"prisma": "^3.15.1",
"rimraf": "^3.0.2",
"tsup": "^6.0.1",
"typescript": "^4.6.3"
Expand Down
6 changes: 3 additions & 3 deletions packages/server/src/api/report/report.processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ export class ReportProcessor {
users: {
connectOrCreate: {
where: {
issueId_userId: {
issueId_eventUserId: {
issueId: issueIntro,
userId: userIntro,
eventUserId: userIntro,
},
},
create: {
user: {
eventUser: {
connectOrCreate: {
where: { id: userIntro },
create: {
Expand Down
4 changes: 2 additions & 2 deletions packages/types/src/interfaces/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import type { Result } from 'source-map-trace/dist/interfaces'
export interface OhbugEventLike extends Omit<OhbugEvent<any>, 'user'> {
id?: string
user: OhbugUser
createdAt: Date
issueId: string
createdAt?: Date
issueId?: string
}

export type { OhbugUser }
Expand Down
3 changes: 3 additions & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@
},
"dependencies": {
"@heroicons/react": "^1.0.6",
"@next-auth/prisma-adapter": "^1.0.3",
"clsx": "^1.1.1",
"daisyui": "^2.15.2",
"highcharts": "^10.1.0",
"highcharts-react-official": "^3.1.0",
"jotai": "^1.7.1",
"next": "12.1.6",
"next-auth": "^4.4.0",
"react": "18.1.0",
"react-dom": "18.1.0",
"react-hook-form": "^7.31.3",
"react-use": "^17.4.0",
"swr": "^1.3.0",
"types": "workspace:*",
Expand Down
Binary file added packages/web/public/github.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions packages/web/src/components/loginButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { FC } from 'react'
import { signIn, signOut, useSession } from 'next-auth/react'

const LoginButton: FC = () => {
const { data: session } = useSession()
if (session) {
return (
<>
Signed in as {session.user?.email} <br />
<button
className="btn"
onClick={() => signOut()}
>
Sign out
</button>
</>
)
}
return (
<>
Not signed in <br />
<button
className="btn"
onClick={() => signIn()}
>
Sign in
</button>
</>
)
}

export default LoginButton
31 changes: 22 additions & 9 deletions packages/web/src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import type { NextPage } from 'next'
import type { ReactElement, ReactNode } from 'react'
import type { AppProps } from 'next/app'
import { SessionProvider } from 'next-auth/react'
import { SWRConfig } from 'swr'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
Expand All @@ -7,16 +10,26 @@ import '~/styles/globals.css'

dayjs.extend(relativeTime)

function MyApp({ Component, pageProps }: AppProps) {
return (
<SWRConfig
value={{ fetcher: (resource, init) => fetch(resource, init).then(res => res.json()) }}
>
<Layout>
type NextPageWithLayout = NextPage & {
getLayout?: (page: ReactElement) => ReactNode
}

type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}

function MyApp({ Component, pageProps: { session, ...pageProps } }: AppPropsWithLayout) {
const getLayout = Component.getLayout ?? (page => <Layout>{page}</Layout>)

return getLayout((
<SessionProvider session={session}>
<SWRConfig
value={{ fetcher: (resource, init) => fetch(resource, init).then(res => res.json()) }}
>
<Component {...pageProps} />
</Layout>
</SWRConfig>
)
</SWRConfig>
</SessionProvider>
))
}

export default MyApp
24 changes: 24 additions & 0 deletions packages/web/src/pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { NextApiRequest, NextApiResponse } from 'next'
import NextAuth from 'next-auth'
import GithubProvider from 'next-auth/providers/github'
import { PrismaAdapter } from '@next-auth/prisma-adapter'
import { serviceGetSetting } from '~/services/bootstrap'
import { prisma } from '~/db'

export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const setting = await serviceGetSetting()
return NextAuth(req, res, {
adapter: PrismaAdapter(prisma),
providers: [
GithubProvider({
clientId: setting?.githubClientId,
clientSecret: setting?.githubClientSecret,
httpOptions: { timeout: 40000 },
}),
],
pages: { signIn: '/auth/signin' },
})
}
15 changes: 15 additions & 0 deletions packages/web/src/pages/api/setting/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Setting } from '@prisma/client'
import type { NextApiRequest, NextApiResponse } from 'next'
import { serviceCreateSetting } from '~/services/bootstrap'

export default async function handler(
req: NextApiRequest,
res: NextApiResponse<Setting>,
) {
const { method } = req

if (method === 'POST') {
const result = await serviceCreateSetting(req.body)
res.status(200).json(result)
}
}
2 changes: 1 addition & 1 deletion packages/web/src/pages/api/trends/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { serviceGetIssuesTrends } from '~/services/issues'

export default async function handler(
req: NextApiRequest,
res: NextApiResponse<serviceGetIssuesTrendsReturn[]>,
res: NextApiResponse<serviceGetIssuesTrendsReturn>,
) {
const ids = req.query.ids as string
const type = (req.query.type || '24h') as '24h' | '14d'
Expand Down
52 changes: 52 additions & 0 deletions packages/web/src/pages/auth/signin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { GetServerSideProps } from 'next'
import type { ClientSafeProvider } from 'next-auth/react'
import { getProviders, signIn } from 'next-auth/react'
import type { FC } from 'react'

interface Props {
providers: ClientSafeProvider[]
}

const Signin: FC<Props> = ({ providers }) => {
return (
<div className="hero">
<div className="hero-content flex-col">
<div className="text-center">
<h1 className="text-5xl font-bold">Ohbug</h1>
<p className="py-6">An open source application information monitoring platform.</p>
</div>
<div className="card flex-shrink-0 w-full max-w-sm shadow-2xl bg-base-100">
<div className="card-body">
{/* <div className="form-control">
<label className="label">
<span className="label-text">Email</span>
</label>
<input
className="input input-bordered"
placeholder="email"
type="text"
/>
</div> */}
{providers?.map(provider => (
<button
className='btn'
key={provider.name}
onClick={() => signIn(provider.id)}
>
Sign in with{' '}{provider.name}
</button>
))}
</div>
</div>
</div>
</div>
)
}

export default Signin

export const getServerSideProps: GetServerSideProps<Props> = async() => {
const providers = await getProviders()

return { props: { providers: JSON.parse(JSON.stringify(Object.values(providers ?? {}))) } }
}
87 changes: 87 additions & 0 deletions packages/web/src/pages/bootstrap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import type { Setting } from '@prisma/client'
import { useRouter } from 'next/router'
import type { ReactElement } from 'react'
import { useCallback } from 'react'
import { useForm } from 'react-hook-form'

const Bootstrap = () => {
const router = useRouter()
const { register, handleSubmit } = useForm<Setting>()
const onSubmit = useCallback((data: Setting) => {
fetch(
'/api/setting',
{
method: 'POST',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json; charset=utf-8' },
},
)
.then(res => res.json())
.then((setting) => {
if (setting)
router.replace('/auth/signin')
})
}, [])

return (
<div className="hero min-h-screen">
<div className="hero-content">
<div className="card flex-shrink-0 w-96 shadow-2xl bg-base-100">
<div className="card-body">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-control">
<label className="label">
<span className="label-text">Github OAuth Client ID</span>
</label>
<input
className="input input-bordered"
placeholder="Client ID"
required
type="text"
{...register('githubClientId')}
/>
</div>
<div className="form-control">
<label className="label">
<span className="label-text">Github OAuth Client Secret</span>
</label>
<input
className="input input-bordered"
placeholder="Client Secret"
required
type="text"
{...register('githubClientSecret')}
/>
<label className="label">
<a
className="label-text-alt link link-hover"
href="https://docs.github.com/developers/apps/building-oauth-apps/creating-an-oauth-app"
rel="noreferrer"
target="_blank"
>
Get Client ID/Secret?
</a>
</label>
</div>

<div className="form-control mt-6">
<button
className="btn btn-primary"
type="submit"
>
Bootstrap
</button>
</div>
</form>
</div>
</div>
</div>
</div>
)
}

Bootstrap.getLayout = function getLayout(page: ReactElement) {
return page
}

export default Bootstrap
28 changes: 25 additions & 3 deletions packages/web/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
import type { NextPage } from 'next'
import type { Setting } from '@prisma/client'
import type { GetServerSideProps, NextPage } from 'next'
import Head from 'next/head'
import Link from 'next/link'
import Login from '~/components/loginButton'
import { serviceGetSetting } from '~/services/bootstrap'

const Home: NextPage = () => {
interface Props {
setting: Setting | null
}

export const getServerSideProps: GetServerSideProps<Props> = async() => {
const setting = await serviceGetSetting()
if (!setting) {
return {
redirect: {
destination: '/bootstrap',
permanent: false,
},
}
}
return { props: { setting } }
}

const Home: NextPage<Props> = () => {
return (
<div>
<Head>
Expand All @@ -18,7 +38,9 @@ const Home: NextPage = () => {
</Head>

<main>
<Link href="/issues"><a>go to issues</a></Link>
<div><Link href="/issues"><a>go to issues</a></Link></div>

<Login />
</main>
</div>
)
Expand Down
17 changes: 17 additions & 0 deletions packages/web/src/services/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Setting } from '@prisma/client'
import { prisma } from '~/db'

export function serviceGetSetting() {
return prisma.setting.findFirst()
}

export async function serviceCreateSetting(data: Setting) {
const setting = await prisma.setting.findFirst()
if (setting) {
return prisma.setting.update({
data,
where: { id: setting.id },
})
}
return prisma.setting.create({ data })
}
Loading

0 comments on commit d26286d

Please sign in to comment.