add credentials provider

上级 8cf50584
import prisma from '@/lib/prisma';
import { hash } from 'bcrypt';
import { NextResponse } from 'next/server';
export async function POST(req: Request) {
try {
const { name, email, password } = (await req.json()) as {
name: string;
email: string;
password: string;
};
if (!name || !email || !password) {
throw { message: 'all fields are required' };
}
const hashedPassword = await hash(password, 12);
const user = await prisma.user.create({
data: {
name,
email: email.toLowerCase(),
password: hashedPassword,
},
});
return NextResponse.json({
user: {
name: user.name,
email: user.email,
},
});
} catch (error: any) {
return new NextResponse(
JSON.stringify({
status: 'error',
message: error.message,
}),
{ status: 500 }
);
}
}
\ No newline at end of file
'use client';
import Link from '@/components/Link';
import { useRouter } from 'next/navigation';
import { useSearchParams, useRouter } from 'next/navigation';
import Icon from '@/components/Icon';
import { signIn } from 'next-auth/react';
import { ChangeEvent, useState } from 'react';
export default function Signin() {
const router = useRouter();'/';
const router = useRouter();
const [loading, setLoading] = useState(false);
const [formValues, setFormValues] = useState({
email: '',
password: '',
});
const [error, setError] = useState('');
const handleLogin = async (provider: 'github' | 'google'): Promise<void> => {
await signIn(provider, {callbackUrl: '/'});
const searchParams = useSearchParams();
const callbackUrl = searchParams.get('callbackUrl') || '/';
const onSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
setLoading(true);
setFormValues({ email: '', password: '' });
const res = await signIn('credentials', {
redirect: false,
email: formValues.email,
password: formValues.password,
callbackUrl,
});
setLoading(false);
if (!res?.error) {
router.push(callbackUrl);
} else {
setError('invalid email or password');
}
} catch (error: any) {
setLoading(false);
setError(error);
}
};
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
setFormValues({ ...formValues, [name]: value });
};
return (
<>
<div className="text-center mb-4">
......@@ -20,21 +57,47 @@ export default function Signin() {
<div className="flex-column card card-md">
<div className="card-body">
<h2 className="h2 text-center mb-4">Login to your account</h2>
<form>
{
error &&
<p className="text-center" style={{color: 'red'}}>{error}</p>
}
<form onSubmit={onSubmit}>
<div className="mb-3">
<label className="form-label">Email address</label>
<input type="email" className="form-control" placeholder="your@email.com"/>
<input
required
type="email"
name="email"
value={formValues.email}
onChange={handleChange}
className="form-control"
placeholder="your@email.com"
/>
</div>
<div className="mb-2">
<label className="form-label">
Password
</label>
<div className="input-group input-group-flat">
<input type="password" className="form-control" placeholder="Your password"/>
<input
required
type="password"
name="password"
value={formValues.password}
onChange={handleChange}
className="form-control"
placeholder="Your password"
/>
</div>
</div>
<div className="form-footer">
<button className="btn btn-primary w-100">Sign in</button>
<button
type="submit"
className="btn btn-primary w-100"
disabled={loading}
>
{loading ? 'loading...' : 'Sign In'}
</button>
</div>
</form>
</div>
......@@ -42,13 +105,13 @@ export default function Signin() {
<div className="card-body">
<div className="row">
<div className="col">
<a onClick={() => handleLogin('github')} className="btn w-100">
<a onClick={() => signIn('github', { callbackUrl })} className="btn w-100">
<Icon name="brand-github"/>
Login with Github
</a>
</div>
<div className="col">
<a onClick={() => handleLogin('google')} className="btn w-100">
<a onClick={() => signIn('google', { callbackUrl })} className="btn w-100">
<Icon name="brand-google"/>
Login with Google
</a>
......
......@@ -2,30 +2,104 @@
import Link from '@/components/Link';
import { useRouter } from 'next/navigation';
import { ChangeEvent, useState } from 'react';
import { signIn } from 'next-auth/react';
export default function Signup() {
const router = useRouter();
const [loading, setLoading] = useState(false);
const [formValues, setFormValues] = useState({
name: '',
email: '',
password: '',
});
const [error, setError] = useState('');
const onSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setFormValues({ name: '', email: '', password: '' });
try {
const res = await fetch('/api/register', {
method: 'POST',
body: JSON.stringify(formValues),
headers: {
'Content-Type': 'application/json',
},
});
setLoading(false);
if (!res.ok) {
setError((await res.json()).message);
return;
}
signIn(undefined, { callbackUrl: '/' });
} catch (error: any) {
setLoading(false);
setError(error);
}
};
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
setFormValues({ ...formValues, [name]: value });
};
return (
<>
<div className="text-center mb-4">
<Link href="/" className="mx-auto d-inline-block logo" aria-label="Tabler" />
</div>
<form className="flex-column card card-md">
<form onSubmit={onSubmit} className="flex-column card card-md">
<div className="card-body">
<h2 className="card-title text-center mb-4">Create new account</h2>
{
error &&
<p className="text-center" style={{color: 'red'}}>{error}</p>
}
<div className="mb-3">
<label className="form-label">Name</label>
<input
required
type="name"
name="name"
value={formValues.name}
onChange={handleChange}
className="form-control"
placeholder="Name"
/>
</div>
<div className="mb-3">
<label className="form-label">Email address</label>
<input type="email" className="form-control" placeholder="Enter email"/>
<input
required
type="email"
name="email"
value={formValues.email}
onChange={handleChange}
className="form-control"
placeholder="Enter email"
/>
</div>
<div className="mb-3">
<label className="form-label">Password</label>
<div className="input-group input-group-flat">
<input type="password" className="form-control" placeholder="Password"/>
<input
type="password"
name="password"
value={formValues.password}
onChange={handleChange}
className="form-control"
placeholder="Password"
/>
</div>
</div>
<div className="form-footer">
<button className="btn btn-primary w-100">Create new account</button>
<button className="btn btn-primary w-100" disabled={loading}>
{loading ? 'loading...' : 'Create new account'}
</button>
</div>
</div>
</form>
......
......@@ -2,22 +2,92 @@ import prisma from '@/lib/prisma';
import { PrismaAdapter } from '@next-auth/prisma-adapter';
import GitHubProvider from 'next-auth/providers/github';
import GoogleProvider from 'next-auth/providers/google';
import CredentialsProvider from 'next-auth/providers/credentials';
import { User } from '@prisma/client';
import { compare } from 'bcrypt';
export const authConfig = {
providers: [
GitHubProvider({
clientId: process.env.GITHUB_ID as string,
clientSecret: process.env.GITHUB_SECRET as string,
}),
GoogleProvider({
clientId: process.env.GOOGLE_ID as string,
clientSecret: process.env.GOOGLE_SECRET as string,
}),
CredentialsProvider({
name: 'Login to your account',
credentials: {
email: {
label: 'Email address',
type: 'text',
placeholder: 'your@email.com"'
},
password: {
label: 'Password',
type: 'password',
placeholder: 'Your password'
},
},
async authorize(credentials) {
if (!credentials || !credentials.email || !credentials.password) {
return null;
}
const user = await prisma.user.findUnique({
where: { email: credentials.email }
});
if (!user) {
return null;
}
const isPasswordValid = await compare(
credentials.password,
user.password
);
if (!isPasswordValid) {
return null;
}
const { password, ...userWithoutPassword } = user;
return userWithoutPassword as User;
}
}),
GitHubProvider({
clientId: process.env.GITHUB_ID as string,
clientSecret: process.env.GITHUB_SECRET as string,
}),
GoogleProvider({
clientId: process.env.GOOGLE_ID as string,
clientSecret: process.env.GOOGLE_SECRET as string,
}),
],
pages: {
signIn: '/signin',
signIn: '/signin'
},
adapter: PrismaAdapter(prisma),
secret: process.env.NEXTAUTH_SECRET,
database: process.env.POSTGRES_PRISMA_URL,
session: {
strategy: 'jwt',
},
callbacks: {
session: ({ session, token }) => {
return {
...session,
user: {
...session.user,
id: token.id,
randomKey: token.randomKey,
},
};
},
jwt: ({ token, user }) => {
if (user) {
const u = user as unknown as any;
return {
...token,
id: u.id,
randomKey: u.randomKey,
};
}
return token;
},
},
};
......@@ -48,6 +48,7 @@
"acorn-jsx": "^5.3.2",
"aos": "^2.3.4",
"autoprefixer": "^10.4.14",
"bcrypt": "^5.1.1",
"clsx": "^2.0.0",
"concurrently": "^8.2.0",
"contentlayer": "^0.3.4",
......
......@@ -86,6 +86,9 @@ dependencies:
autoprefixer:
specifier: ^10.4.14
version: 10.4.14(postcss@8.4.27)
bcrypt:
specifier: ^5.1.1
version: 5.1.1
clsx:
specifier: ^2.0.0
version: 2.0.0
......@@ -209,6 +212,9 @@ dependencies:
react-syntax-highlighter:
specifier: ^15.5.0
version: 15.5.0(react@18.2.0)
react-toastify:
specifier: ^9.1.3
version: 9.1.3(react-dom@18.2.0)(react@18.2.0)
rehype-autolink-headings:
specifier: ^6.1.1
version: 6.1.1
......@@ -2218,6 +2224,24 @@ packages:
tslib: 2.4.1
dev: false
/@mapbox/node-pre-gyp@1.0.11:
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
hasBin: true
dependencies:
detect-libc: 2.0.2
https-proxy-agent: 5.0.1
make-dir: 3.1.0
node-fetch: 2.6.13
nopt: 5.0.0
npmlog: 5.0.1
rimraf: 3.0.2
semver: 7.5.4
tar: 6.1.15
transitivePeerDependencies:
- encoding
- supports-color
dev: false
/@mdx-js/esbuild@2.3.0(esbuild@0.17.18):
resolution: {integrity: sha512-r/vsqsM0E+U4Wr0DK+0EfmABE/eg+8ITW4DjvYdh3ve/tK2safaqHArNnaqbOk1DjYGrhxtoXoGaM3BY8fGBTA==}
peerDependencies:
......@@ -3290,6 +3314,15 @@ packages:
hasBin: true
dev: false
/agent-base@6.0.2:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
dependencies:
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: false
/ajv-keywords@3.5.2(ajv@6.12.6):
resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
peerDependencies:
......@@ -3342,6 +3375,18 @@ packages:
lodash.throttle: 4.1.1
dev: false
/aproba@2.0.0:
resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
dev: false
/are-we-there-yet@2.0.0:
resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
engines: {node: '>=10'}
dependencies:
delegates: 1.0.0
readable-stream: 3.6.2
dev: false
/argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
dependencies:
......@@ -3511,6 +3556,18 @@ packages:
resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==}
dev: false
/bcrypt@5.1.1:
resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==}
engines: {node: '>= 10.0.0'}
requiresBuild: true
dependencies:
'@mapbox/node-pre-gyp': 1.0.11
node-addon-api: 5.1.0
transitivePeerDependencies:
- encoding
- supports-color
dev: false
/big.js@5.2.2:
resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
dev: false
......@@ -3675,6 +3732,11 @@ packages:
fsevents: 2.3.2
dev: false
/chownr@2.0.0:
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
engines: {node: '>=10'}
dev: false
/chrome-trace-event@1.0.3:
resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
engines: {node: '>=6.0'}
......@@ -3712,6 +3774,11 @@ packages:
wrap-ansi: 7.0.0
dev: false
/clsx@1.2.1:
resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
engines: {node: '>=6'}
dev: false
/clsx@2.0.0:
resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==}
engines: {node: '>=6'}
......@@ -3738,6 +3805,11 @@ packages:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
dev: false
/color-support@1.1.3:
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
hasBin: true
dev: false
/comma-separated-tokens@1.0.8:
resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==}
dev: false
......@@ -3808,6 +3880,10 @@ packages:
proto-list: 1.2.4
dev: false
/console-control-strings@1.1.0:
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
dev: false
/contentlayer@0.3.4(esbuild@0.17.18)(markdown-wasm@1.2.0):
resolution: {integrity: sha512-FYDdTUFaN4yqep0waswrhcXjmMJnPD5iXDTtxcUCGdklfuIrXM2xLx51xl748cHmGA6IsC+27YZFxU6Ym13QIA==}
engines: {node: '>=14.18'}
......@@ -4022,11 +4098,20 @@ packages:
object-keys: 1.1.1
dev: false
/delegates@1.0.0:
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
dev: false
/dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
dev: false
/detect-libc@2.0.2:
resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
engines: {node: '>=8'}
dev: false
/devlop@1.1.0:
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
dependencies:
......@@ -4786,6 +4871,13 @@ packages:
universalify: 2.0.0
dev: false
/fs-minipass@2.1.0:
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
engines: {node: '>= 8'}
dependencies:
minipass: 3.3.6
dev: false
/fs-monkey@1.0.3:
resolution: {integrity: sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==}
dev: false
......@@ -4820,6 +4912,21 @@ packages:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
dev: false
/gauge@3.0.2:
resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
engines: {node: '>=10'}
dependencies:
aproba: 2.0.0
color-support: 1.1.3
console-control-strings: 1.1.0
has-unicode: 2.0.1
object-assign: 4.1.1
signal-exit: 3.0.7
string-width: 4.2.3
strip-ansi: 6.0.1
wide-align: 1.1.5
dev: false
/gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
......@@ -5026,6 +5133,10 @@ packages:
has-symbols: 1.0.3
dev: false
/has-unicode@2.0.1:
resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
dev: false
/has@1.0.3:
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
engines: {node: '>= 0.4.0'}
......@@ -5256,6 +5367,16 @@ packages:
resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==}
dev: false
/https-proxy-agent@5.0.1:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'}
dependencies:
agent-base: 6.0.2
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: false
/ignore@5.2.4:
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
engines: {node: '>= 4'}
......@@ -5819,6 +5940,13 @@ packages:
yallist: 4.0.0
dev: false
/make-dir@3.1.0:
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
engines: {node: '>=8'}
dependencies:
semver: 6.3.0
dev: false
/map-obj@1.0.1:
resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==}
engines: {node: '>=0.10.0'}
......@@ -6852,6 +6980,32 @@ packages:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
dev: false
/minipass@3.3.6:
resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
engines: {node: '>=8'}
dependencies:
yallist: 4.0.0
dev: false
/minipass@5.0.0:
resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
engines: {node: '>=8'}
dev: false
/minizlib@2.1.2:
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
engines: {node: '>= 8'}
dependencies:
minipass: 3.3.6
yallist: 4.0.0
dev: false
/mkdirp@1.0.4:
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
engines: {node: '>=10'}
hasBin: true
dev: false
/modern-async@1.1.3:
resolution: {integrity: sha512-2ErvrRBESpOvzofl3r4Wk3J8ykMvAcTyFPciZKk8I8IvRzXEwNT9Z2H5lZ62wVjNtPUvfjKRYIvAeNrLGLIksw==}
dependencies:
......@@ -7050,11 +7204,27 @@ packages:
tslib: 2.4.1
dev: false
/node-addon-api@5.1.0:
resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==}
dev: false
/node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
dev: false
/node-fetch@2.6.13:
resolution: {integrity: sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
dependencies:
whatwg-url: 5.0.0
dev: false
/node-fetch@3.3.1:
resolution: {integrity: sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
......@@ -7068,6 +7238,14 @@ packages:
resolution: {integrity: sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==}
dev: false
/nopt@5.0.0:
resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
engines: {node: '>=6'}
hasBin: true
dependencies:
abbrev: 1.1.1
dev: false
/nopt@6.0.0:
resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
......@@ -7099,6 +7277,15 @@ packages:
resolution: {integrity: sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA==}
dev: false
/npmlog@5.0.1:
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
dependencies:
are-we-there-yet: 2.0.0
console-control-strings: 1.1.0
gauge: 3.0.2
set-blocking: 2.0.0
dev: false
/nprogress@0.2.0:
resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==}
dev: false
......@@ -7579,6 +7766,17 @@ packages:
refractor: 3.6.0
dev: false
/react-toastify@9.1.3(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==}
peerDependencies:
react: '>=16'
react-dom: '>=16'
dependencies:
clsx: 1.2.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/react@18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'}
......@@ -7607,6 +7805,15 @@ packages:
path-type: 1.1.0
dev: false
/readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
dependencies:
inherits: 2.0.4
string_decoder: 1.3.0
util-deprecate: 1.0.2
dev: false
/readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
......@@ -8149,6 +8356,10 @@ packages:
randombytes: 2.1.0
dev: false
/set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
dev: false
/shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
......@@ -8301,6 +8512,12 @@ packages:
es-abstract: 1.21.0
dev: false
/string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
dependencies:
safe-buffer: 5.2.1
dev: false
/stringify-entities@4.0.3:
resolution: {integrity: sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==}
dependencies:
......@@ -8425,6 +8642,18 @@ packages:
engines: {node: '>=6'}
dev: false
/tar@6.1.15:
resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==}
engines: {node: '>=10'}
dependencies:
chownr: 2.0.0
fs-minipass: 2.1.0
minipass: 5.0.0
minizlib: 2.1.2
mkdirp: 1.0.4
yallist: 4.0.0
dev: false
/terser-webpack-plugin@5.3.7(esbuild@0.17.18)(webpack@5.88.2):
resolution: {integrity: sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==}
engines: {node: '>= 10.13.0'}
......@@ -8496,6 +8725,10 @@ packages:
resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==}
dev: false
/tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
dev: false
/tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
......@@ -8781,6 +9014,10 @@ packages:
punycode: 2.1.1
dev: false
/util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
dev: false
/uuid@8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
hasBin: true
......@@ -8859,6 +9096,10 @@ packages:
engines: {node: '>= 8'}
dev: false
/webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
dev: false
/webpack-sources@3.2.3:
resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
engines: {node: '>=10.13.0'}
......@@ -8904,6 +9145,13 @@ packages:
- uglify-js
dev: false
/whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
dev: false
/which-boxed-primitive@1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
dependencies:
......@@ -8934,6 +9182,12 @@ packages:
isexe: 2.0.0
dev: false
/wide-align@1.1.5:
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
dependencies:
string-width: 4.2.3
dev: false
/word-wrap@1.2.3:
resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
engines: {node: '>=0.10.0'}
......
......@@ -33,6 +33,7 @@ CREATE TABLE "users" (
"name" TEXT,
"email" TEXT,
"email_verified" TIMESTAMP(3),
"password" TEXT,
"image" TEXT,
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
......
......@@ -45,6 +45,7 @@ model User {
name String?
email String? @unique
emailVerified DateTime? @map("email_verified")
password String?
image String?
accounts Account[]
sessions Session[]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册