diff --git a/package.json b/package.json index 02e0555..c269834 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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" diff --git a/packages/server/src/api/report/report.processor.ts b/packages/server/src/api/report/report.processor.ts index 0873092..cacb2b8 100644 --- a/packages/server/src/api/report/report.processor.ts +++ b/packages/server/src/api/report/report.processor.ts @@ -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: { diff --git a/packages/types/src/interfaces/event.ts b/packages/types/src/interfaces/event.ts index e8ca5d4..b20778f 100644 --- a/packages/types/src/interfaces/event.ts +++ b/packages/types/src/interfaces/event.ts @@ -4,8 +4,8 @@ import type { Result } from 'source-map-trace/dist/interfaces' export interface OhbugEventLike extends Omit, 'user'> { id?: string user: OhbugUser - createdAt: Date - issueId: string + createdAt?: Date + issueId?: string } export type { OhbugUser } diff --git a/packages/web/package.json b/packages/web/package.json index a183ec4..d790cd9 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -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:*", diff --git a/packages/web/public/github.png b/packages/web/public/github.png new file mode 100644 index 0000000..182a1a3 Binary files /dev/null and b/packages/web/public/github.png differ diff --git a/packages/web/src/components/loginButton.tsx b/packages/web/src/components/loginButton.tsx new file mode 100644 index 0000000..77af30e --- /dev/null +++ b/packages/web/src/components/loginButton.tsx @@ -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}
+ + + ) + } + return ( + <> + Not signed in
+ + + ) +} + +export default LoginButton diff --git a/packages/web/src/pages/_app.tsx b/packages/web/src/pages/_app.tsx index 487410c..5d11c33 100644 --- a/packages/web/src/pages/_app.tsx +++ b/packages/web/src/pages/_app.tsx @@ -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' @@ -7,16 +10,26 @@ import '~/styles/globals.css' dayjs.extend(relativeTime) -function MyApp({ Component, pageProps }: AppProps) { - return ( - fetch(resource, init).then(res => res.json()) }} - > - +type NextPageWithLayout = NextPage & { + getLayout?: (page: ReactElement) => ReactNode +} + +type AppPropsWithLayout = AppProps & { + Component: NextPageWithLayout +} + +function MyApp({ Component, pageProps: { session, ...pageProps } }: AppPropsWithLayout) { + const getLayout = Component.getLayout ?? (page => {page}) + + return getLayout(( + + fetch(resource, init).then(res => res.json()) }} + > - - - ) + + + )) } export default MyApp diff --git a/packages/web/src/pages/api/auth/[...nextauth].ts b/packages/web/src/pages/api/auth/[...nextauth].ts new file mode 100644 index 0000000..d7ad934 --- /dev/null +++ b/packages/web/src/pages/api/auth/[...nextauth].ts @@ -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' }, + }) +} diff --git a/packages/web/src/pages/api/setting/index.ts b/packages/web/src/pages/api/setting/index.ts new file mode 100644 index 0000000..9adf007 --- /dev/null +++ b/packages/web/src/pages/api/setting/index.ts @@ -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, +) { + const { method } = req + + if (method === 'POST') { + const result = await serviceCreateSetting(req.body) + res.status(200).json(result) + } +} diff --git a/packages/web/src/pages/api/trends/index.ts b/packages/web/src/pages/api/trends/index.ts index 746d557..aab8cb8 100644 --- a/packages/web/src/pages/api/trends/index.ts +++ b/packages/web/src/pages/api/trends/index.ts @@ -4,7 +4,7 @@ import { serviceGetIssuesTrends } from '~/services/issues' export default async function handler( req: NextApiRequest, - res: NextApiResponse, + res: NextApiResponse, ) { const ids = req.query.ids as string const type = (req.query.type || '24h') as '24h' | '14d' diff --git a/packages/web/src/pages/auth/signin.tsx b/packages/web/src/pages/auth/signin.tsx new file mode 100644 index 0000000..5c3d229 --- /dev/null +++ b/packages/web/src/pages/auth/signin.tsx @@ -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 = ({ providers }) => { + return ( +
+
+
+

Ohbug

+

An open source application information monitoring platform.

+
+
+
+ {/*
+ + +
*/} + {providers?.map(provider => ( + + ))} +
+
+
+
+ ) +} + +export default Signin + +export const getServerSideProps: GetServerSideProps = async() => { + const providers = await getProviders() + + return { props: { providers: JSON.parse(JSON.stringify(Object.values(providers ?? {}))) } } +} diff --git a/packages/web/src/pages/bootstrap.tsx b/packages/web/src/pages/bootstrap.tsx new file mode 100644 index 0000000..e59f0f7 --- /dev/null +++ b/packages/web/src/pages/bootstrap.tsx @@ -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() + 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 ( +
+
+
+
+
+
+ + +
+
+ + + +
+ +
+ +
+
+
+
+
+
+ ) +} + +Bootstrap.getLayout = function getLayout(page: ReactElement) { + return page +} + +export default Bootstrap diff --git a/packages/web/src/pages/index.tsx b/packages/web/src/pages/index.tsx index 3164385..6c74d7d 100644 --- a/packages/web/src/pages/index.tsx +++ b/packages/web/src/pages/index.tsx @@ -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 = async() => { + const setting = await serviceGetSetting() + if (!setting) { + return { + redirect: { + destination: '/bootstrap', + permanent: false, + }, + } + } + return { props: { setting } } +} + +const Home: NextPage = () => { return (
@@ -18,7 +38,9 @@ const Home: NextPage = () => {
- go to issues + + +
) diff --git a/packages/web/src/services/bootstrap.ts b/packages/web/src/services/bootstrap.ts new file mode 100644 index 0000000..db71002 --- /dev/null +++ b/packages/web/src/services/bootstrap.ts @@ -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 }) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c12a6e7..aa83f0f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,7 +15,7 @@ importers: '@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 '@types/node': ^17.0.38 '@types/ramda': ^0.28.13 commitizen: ^4.2.4 @@ -27,7 +27,7 @@ importers: esno: ^0.16.3 husky: ^8.0.1 lint-staged: ^12.5.0 - prisma: ^3.11.1 + prisma: ^3.15.1 ramda: ^0.28.0 rimraf: ^3.0.2 source-map-trace: ^0.1.3 @@ -39,7 +39,7 @@ importers: '@ohbug/react': 2.0.3 '@ohbug/types': 2.0.3 '@ohbug/vue': 2.0.3 - '@prisma/client': 3.14.0_prisma@3.14.0 + '@prisma/client': 3.15.1_prisma@3.15.1 dayjs: 1.11.2 dotenv: 16.0.1 ramda: 0.28.0 @@ -60,7 +60,7 @@ importers: esno: 0.16.3 husky: 8.0.1 lint-staged: 12.5.0 - prisma: 3.14.0 + prisma: 3.15.1 rimraf: 3.0.2 tsup: 6.0.1_typescript@4.7.2 typescript: 4.7.2 @@ -125,6 +125,7 @@ importers: packages/web: specifiers: '@heroicons/react': ^1.0.6 + '@next-auth/prisma-adapter': ^1.0.3 '@tailwindcss/line-clamp': ^0.4.0 '@types/node': ^17.0.38 '@types/react': 18.0.10 @@ -138,9 +139,11 @@ importers: highcharts-react-official: ^3.1.0 jotai: ^1.7.1 next: 12.1.6 + next-auth: ^4.4.0 postcss: ^8.4.14 react: 18.1.0 react-dom: 18.1.0 + react-hook-form: ^7.31.3 react-use: ^17.4.0 superjson: ^1.9.1 swr: ^1.3.0 @@ -150,14 +153,17 @@ importers: zustand: 4.0.0-rc.1 dependencies: '@heroicons/react': 1.0.6_react@18.1.0 + '@next-auth/prisma-adapter': 1.0.3_next-auth@4.4.0 clsx: 1.1.1 daisyui: 2.15.2_ugi4xkrfysqkt4c4y6hkyfj344 highcharts: 10.1.0 highcharts-react-official: 3.1.0_ssqge53ewyv7nlqf3cr3jpevty jotai: 1.7.1_react@18.1.0 next: 12.1.6_ef5jwxihqo6n7gxfmzogljlgcm + next-auth: 4.4.0_ef5jwxihqo6n7gxfmzogljlgcm react: 18.1.0 react-dom: 18.1.0_react@18.1.0 + react-hook-form: 7.31.3_react@18.1.0 react-use: 17.4.0_ef5jwxihqo6n7gxfmzogljlgcm swr: 1.3.0_react@18.1.0 types: link:../types @@ -1059,6 +1065,15 @@ packages: - chokidar dev: true + /@next-auth/prisma-adapter/1.0.3_next-auth@4.4.0: + resolution: {integrity: sha512-3Lq1cD3ytKM3EGKJZ4UZvlqshLtlPvYxLeCrUV9ifYwYlq51kmDaHjsIawlp8EbH5pE1UhlsvtlXMery7RghtA==} + peerDependencies: + '@prisma/client': '>=2.26.0 || >=3' + next-auth: ^4.0.1 + dependencies: + next-auth: 4.4.0_ef5jwxihqo6n7gxfmzogljlgcm + dev: false + /@next/env/12.1.6: resolution: {integrity: sha512-Te/OBDXFSodPU6jlXYPAXpmZr/AkG6DCATAxttQxqOWaq6eDFX25Db3dK0120GZrSZmv4QCe9KsZmJKDbWs4OA==} @@ -1232,8 +1247,12 @@ packages: '@ohbug/types': 2.0.3 dev: false - /@prisma/client/3.14.0_prisma@3.14.0: - resolution: {integrity: sha512-atb41UpgTR1MCst0VIbiHTMw8lmXnwUvE1KyUCAkq08+wJyjRE78Due+nSf+7uwqQn+fBFYVmoojtinhlLOSaA==} + /@panva/hkdf/1.0.2: + resolution: {integrity: sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==} + dev: false + + /@prisma/client/3.15.1_prisma@3.15.1: + resolution: {integrity: sha512-Lsk7oupvO9g99mrIs07iE6BIMouHs46Yq/YY8itTsUQNKfecsPuZvVYvcKci0pqRQ0neOpvIvoA/ouZmIMBCrQ==} engines: {node: '>=12.6'} requiresBuild: true peerDependencies: @@ -1242,16 +1261,16 @@ packages: prisma: optional: true dependencies: - '@prisma/engines-version': 3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a - prisma: 3.14.0 + '@prisma/engines-version': 3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e + prisma: 3.15.1 dev: false - /@prisma/engines-version/3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a: - resolution: {integrity: sha512-D+yHzq4a2r2Rrd0ZOW/mTZbgDIkUkD8ofKgusEI1xPiZz60Daks+UM7Me2ty5FzH3p/TgyhBpRrfIHx+ha20RQ==} + /@prisma/engines-version/3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e: + resolution: {integrity: sha512-e3k2Vd606efd1ZYy2NQKkT4C/pn31nehyLhVug6To/q8JT8FpiMrDy7zmm3KLF0L98NOQQcutaVtAPhzKhzn9w==} dev: false - /@prisma/engines/3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a: - resolution: {integrity: sha512-LwZvI3FY6f43xFjQNRuE10JM5R8vJzFTSmbV9X0Wuhv9kscLkjRlZt0BEoiHmO+2HA3B3xxbMfB5du7ZoSFXGg==} + /@prisma/engines/3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e: + resolution: {integrity: sha512-NHlojO1DFTsSi3FtEleL9QWXeSF/UjhCW0fgpi7bumnNZ4wj/eQ+BJJ5n2pgoOliTOGv9nX2qXvmHap7rJMNmg==} requiresBuild: true /@tailwindcss/line-clamp/0.4.0_tailwindcss@3.0.24: @@ -2471,6 +2490,11 @@ packages: through2: 4.0.2 dev: true + /cookie/0.4.2: + resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} + engines: {node: '>= 0.6'} + dev: false + /cookie/0.5.0: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} @@ -4886,6 +4910,10 @@ packages: supports-color: 8.1.1 dev: true + /jose/4.8.1: + resolution: {integrity: sha512-+/hpTbRcCw9YC0TOfN1W47pej4a9lRmltdOVdRLz5FP5UvUq3CenhXjQK7u/8NdMIIShMXYAh9VLPhc7TjhvFw==} + dev: false + /jotai/1.7.1_react@18.1.0: resolution: {integrity: sha512-Ucighxm0GwjvMpnLn7//CrHFSgLoL7LxHhXm7ofUNIktsRE4TAAhzrwsBb0LjJ0DuBJqQEJq9/jbHFfjO0ylYQ==} engines: {node: '>=12.7.0'} @@ -5513,6 +5541,30 @@ packages: winston: 3.7.2 dev: false + /next-auth/4.4.0_ef5jwxihqo6n7gxfmzogljlgcm: + resolution: {integrity: sha512-Lz5ytnkP4OfTUsknhpZVH9eTLaLOZX/UEe6OQESMaZSTBr9cVc99G4kEPG83KrwKMe7VVb5DfDWH0rlod7wMzA==} + engines: {node: ^12.19.0 || ^14.15.0 || ^16.13.0} + peerDependencies: + nodemailer: ^6.6.5 + react: ^17.0.2 || ^18 + react-dom: ^17.0.2 || ^18 + peerDependenciesMeta: + nodemailer: + optional: true + dependencies: + '@babel/runtime': 7.18.0 + '@panva/hkdf': 1.0.2 + cookie: 0.4.2 + jose: 4.8.1 + oauth: 0.9.15 + openid-client: 5.1.6 + preact: 10.7.3 + preact-render-to-string: 5.2.0_preact@10.7.3 + react: 18.1.0 + react-dom: 18.1.0_react@18.1.0 + uuid: 8.3.2 + dev: false + /next-tick/1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} dev: false @@ -5625,11 +5677,20 @@ packages: boolbase: 1.0.0 dev: true + /oauth/0.9.15: + resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} + dev: false + /object-assign/4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} dev: true + /object-hash/2.2.0: + resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} + engines: {node: '>= 6'} + dev: false + /object-hash/3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} @@ -5687,8 +5748,13 @@ packages: es-abstract: 1.20.1 dev: true + /oidc-token-hash/5.0.1: + resolution: {integrity: sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==} + engines: {node: ^10.13.0 || >=12.0.0} + dev: false + /once/1.4.0: - resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 @@ -5712,6 +5778,16 @@ packages: mimic-fn: 2.1.0 dev: true + /openid-client/5.1.6: + resolution: {integrity: sha512-HTFaXWdUHvLFw4GaEMgC0jXYBgpjgzQQNHW1pZsSqJorSgrXzxJ+4u/LWCGaClDEse5HLjXRV+zU5Bn3OefiZw==} + engines: {node: ^12.19.0 || ^14.15.0 || ^16.13.0} + dependencies: + jose: 4.8.1 + lru-cache: 6.0.0 + object-hash: 2.2.0 + oidc-token-hash: 5.0.1 + dev: false + /optionator/0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} engines: {node: '>= 0.8.0'} @@ -6034,6 +6110,19 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /preact-render-to-string/5.2.0_preact@10.7.3: + resolution: {integrity: sha512-+RGwSW78Cl+NsZRUbFW1MGB++didsfqRk+IyRVTaqy+3OjtpKK/6HgBtfszUX0YXMfo41k2iaQSseAHGKEwrbg==} + peerDependencies: + preact: '>=10' + dependencies: + preact: 10.7.3 + pretty-format: 3.8.0 + dev: false + + /preact/10.7.3: + resolution: {integrity: sha512-giqJXP8VbtA1tyGa3f1n9wiN7PrHtONrDyE3T+ifjr/tTkg+2N4d/6sjC9WyJKv8wM7rOYDveqy5ZoFmYlwo4w==} + dev: false + /preferred-pm/3.0.3: resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} engines: {node: '>=10'} @@ -6055,13 +6144,17 @@ packages: hasBin: true dev: true - /prisma/3.14.0: - resolution: {integrity: sha512-l9MOgNCn/paDE+i1K2fp9NZ+Du4trzPTJsGkaQHVBufTGqzoYHuNk8JfzXuIn0Gte6/ZjyKj652Jq/Lc1tp2yw==} + /pretty-format/3.8.0: + resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + dev: false + + /prisma/3.15.1: + resolution: {integrity: sha512-MLO3JUGJpe5+EVisA/i47+zlyF8Ug0ivvGYG4B9oSXQcPiUHB1ccmnpxqR7o0Up5SQgmxkBiEU//HgR6UuIKOw==} engines: {node: '>=12.6'} hasBin: true requiresBuild: true dependencies: - '@prisma/engines': 3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a + '@prisma/engines': 3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e /process-warning/1.0.0: resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} @@ -6138,6 +6231,15 @@ packages: react: 18.1.0 scheduler: 0.22.0 + /react-hook-form/7.31.3_react@18.1.0: + resolution: {integrity: sha512-NVZdCWViIWXXXlQ3jxVQH0NuNfwPf8A/0KvuCxrM9qxtP1qYosfR2ZudarziFrVOC7eTUbWbm1T4OyYCwv9oSQ==} + engines: {node: '>=12.22.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 + dependencies: + react: 18.1.0 + dev: false + /react-is/16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true @@ -7416,7 +7518,7 @@ packages: dev: false /vary/1.1.2: - resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=} + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} dev: false diff --git a/prisma/migrations/20220609083919_create_project/migration.sql b/prisma/migrations/20220609083919_create_project/migration.sql new file mode 100644 index 0000000..a25b6cd --- /dev/null +++ b/prisma/migrations/20220609083919_create_project/migration.sql @@ -0,0 +1,14 @@ +-- CreateTable +CREATE TABLE "Project" ( + "id" SERIAL NOT NULL, + "apiKey" TEXT NOT NULL, + "name" TEXT NOT NULL, + "type" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Project_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Project_name_key" ON "Project"("name"); diff --git a/prisma/migrations/20220609092331_create_setting/migration.sql b/prisma/migrations/20220609092331_create_setting/migration.sql new file mode 100644 index 0000000..73e63b6 --- /dev/null +++ b/prisma/migrations/20220609092331_create_setting/migration.sql @@ -0,0 +1,7 @@ +-- CreateTable +CREATE TABLE "Setting" ( + "id" SERIAL NOT NULL, + "githubClientId" TEXT NOT NULL, + + CONSTRAINT "Setting_pkey" PRIMARY KEY ("id") +); diff --git a/prisma/migrations/20220609143804_/migration.sql b/prisma/migrations/20220609143804_/migration.sql new file mode 100644 index 0000000..a9476ea --- /dev/null +++ b/prisma/migrations/20220609143804_/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Added the required column `githubClientSecret` to the `Setting` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Setting" ADD COLUMN "githubClientSecret" TEXT NOT NULL; diff --git a/prisma/migrations/20220610094826_create_account/migration.sql b/prisma/migrations/20220610094826_create_account/migration.sql new file mode 100644 index 0000000..cc945ac --- /dev/null +++ b/prisma/migrations/20220610094826_create_account/migration.sql @@ -0,0 +1,12 @@ +-- CreateTable +CREATE TABLE "Account" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "avatarUrl" TEXT, + "email" TEXT, + "github" JSONB, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Account_pkey" PRIMARY KEY ("id") +); diff --git a/prisma/migrations/20220613081806_/migration.sql b/prisma/migrations/20220613081806_/migration.sql new file mode 100644 index 0000000..9d27826 --- /dev/null +++ b/prisma/migrations/20220613081806_/migration.sql @@ -0,0 +1,113 @@ +/* + Warnings: + + - The primary key for the `Account` table will be changed. If it partially fails, the table could be left without primary key constraint. + - You are about to drop the column `avatarUrl` on the `Account` table. All the data in the column will be lost. + - You are about to drop the column `createdAt` on the `Account` table. All the data in the column will be lost. + - You are about to drop the column `email` on the `Account` table. All the data in the column will be lost. + - You are about to drop the column `github` on the `Account` table. All the data in the column will be lost. + - You are about to drop the column `name` on the `Account` table. All the data in the column will be lost. + - You are about to drop the column `updatedAt` on the `Account` table. All the data in the column will be lost. + - You are about to drop the column `createdAt` on the `User` table. All the data in the column will be lost. + - You are about to drop the column `ipAddress` on the `User` table. All the data in the column will be lost. + - You are about to drop the column `metadata` on the `User` table. All the data in the column will be lost. + - You are about to drop the column `updatedAt` on the `User` table. All the data in the column will be lost. + - You are about to drop the column `uuid` on the `User` table. All the data in the column will be lost. + - A unique constraint covering the columns `[provider,providerAccountId]` on the table `Account` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[email]` on the table `User` will be added. If there are existing duplicate values, this will fail. + - Added the required column `provider` to the `Account` table without a default value. This is not possible if the table is not empty. + - Added the required column `providerAccountId` to the `Account` table without a default value. This is not possible if the table is not empty. + - Added the required column `type` to the `Account` table without a default value. This is not possible if the table is not empty. + - Added the required column `userId` to the `Account` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropForeignKey +ALTER TABLE "UsersOnIssues" DROP CONSTRAINT "UsersOnIssues_userId_fkey"; + +-- AlterTable +ALTER TABLE "Account" DROP CONSTRAINT "Account_pkey", +DROP COLUMN "avatarUrl", +DROP COLUMN "createdAt", +DROP COLUMN "email", +DROP COLUMN "github", +DROP COLUMN "name", +DROP COLUMN "updatedAt", +ADD COLUMN "access_token" TEXT, +ADD COLUMN "expires_at" INTEGER, +ADD COLUMN "id_token" TEXT, +ADD COLUMN "provider" TEXT NOT NULL, +ADD COLUMN "providerAccountId" TEXT NOT NULL, +ADD COLUMN "refresh_token" TEXT, +ADD COLUMN "scope" TEXT, +ADD COLUMN "session_state" TEXT, +ADD COLUMN "token_type" TEXT, +ADD COLUMN "type" TEXT NOT NULL, +ADD COLUMN "userId" TEXT NOT NULL, +ALTER COLUMN "id" DROP DEFAULT, +ALTER COLUMN "id" SET DATA TYPE TEXT, +ADD CONSTRAINT "Account_pkey" PRIMARY KEY ("id"); +DROP SEQUENCE "Account_id_seq"; + +-- AlterTable +ALTER TABLE "User" DROP COLUMN "createdAt", +DROP COLUMN "ipAddress", +DROP COLUMN "metadata", +DROP COLUMN "updatedAt", +DROP COLUMN "uuid", +ADD COLUMN "emailVerified" TIMESTAMP(3), +ADD COLUMN "image" TEXT; + +-- CreateTable +CREATE TABLE "EventUser" ( + "id" TEXT NOT NULL, + "ipAddress" TEXT NOT NULL, + "uuid" TEXT, + "email" TEXT, + "name" TEXT, + "metadata" JSONB, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "EventUser_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Session" ( + "id" TEXT NOT NULL, + "sessionToken" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "expires" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Session_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "VerificationToken" ( + "identifier" TEXT NOT NULL, + "token" TEXT NOT NULL, + "expires" TIMESTAMP(3) NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken"); + +-- CreateIndex +CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token"); + +-- CreateIndex +CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token"); + +-- CreateIndex +CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- AddForeignKey +ALTER TABLE "UsersOnIssues" ADD CONSTRAINT "UsersOnIssues_userId_fkey" FOREIGN KEY ("userId") REFERENCES "EventUser"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20220613082202_/migration.sql b/prisma/migrations/20220613082202_/migration.sql new file mode 100644 index 0000000..1358f71 --- /dev/null +++ b/prisma/migrations/20220613082202_/migration.sql @@ -0,0 +1,29 @@ +/* + Warnings: + + - You are about to drop the `UsersOnIssues` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropForeignKey +ALTER TABLE "UsersOnIssues" DROP CONSTRAINT "UsersOnIssues_issueId_fkey"; + +-- DropForeignKey +ALTER TABLE "UsersOnIssues" DROP CONSTRAINT "UsersOnIssues_userId_fkey"; + +-- DropTable +DROP TABLE "UsersOnIssues"; + +-- CreateTable +CREATE TABLE "EventUsersOnIssues" ( + "issueId" TEXT NOT NULL, + "eventUserId" TEXT NOT NULL, + "assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "EventUsersOnIssues_pkey" PRIMARY KEY ("issueId","eventUserId") +); + +-- AddForeignKey +ALTER TABLE "EventUsersOnIssues" ADD CONSTRAINT "EventUsersOnIssues_issueId_fkey" FOREIGN KEY ("issueId") REFERENCES "Issue"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "EventUsersOnIssues" ADD CONSTRAINT "EventUsersOnIssues_eventUserId_fkey" FOREIGN KEY ("eventUserId") REFERENCES "EventUser"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5214153..ebe925c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -4,7 +4,8 @@ datasource db { } generator client { - provider = "prisma-client-js" + provider = "prisma-client-js" + previewFeatures = ["filterJson"] } model Event { @@ -35,27 +36,86 @@ model Issue { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt events Event[] - users UsersOnIssues[] + users EventUsersOnIssues[] } -model User { - id String @id +model EventUser { + id String @id ipAddress String uuid String? email String? name String? metadata Json? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - issues UsersOnIssues[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + issues EventUsersOnIssues[] +} + +model EventUsersOnIssues { + issue Issue @relation(fields: [issueId], references: [id]) + issueId String + eventUser EventUser @relation(fields: [eventUserId], references: [id]) + eventUserId String + assignedAt DateTime @default(now()) + + @@id([issueId, eventUserId]) +} + +model Setting { + id Int @id @default(autoincrement()) + githubClientId String + githubClientSecret String +} + +model Project { + id Int @id @default(autoincrement()) + apiKey String + name String @unique + type String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Account { + id String @id @default(cuid()) + userId String + type String + provider String + providerAccountId String + refresh_token String? @db.Text + access_token String? @db.Text + expires_at Int? + token_type String? + scope String? + id_token String? @db.Text + session_state String? + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([provider, providerAccountId]) +} + +model Session { + id String @id @default(cuid()) + sessionToken String @unique + userId String + expires DateTime + user User @relation(fields: [userId], references: [id], onDelete: Cascade) +} + +model User { + id String @id @default(cuid()) + name String? + email String? @unique + emailVerified DateTime? + image String? + accounts Account[] + sessions Session[] } -model UsersOnIssues { - issue Issue @relation(fields: [issueId], references: [id]) - issueId String - user User @relation(fields: [userId], references: [id]) - userId String - assignedAt DateTime @default(now()) +model VerificationToken { + identifier String + token String @unique + expires DateTime - @@id([issueId, userId]) + @@unique([identifier, token]) }