API tokens
This commit is contained in:
parent
b9ab0df7c0
commit
98ad33bcd8
14 changed files with 488 additions and 133 deletions
|
@ -1,6 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { startTransition, useEffect, useRef, useState } from "react"
|
||||
import { startTransition, useEffect, useState } from "react"
|
||||
import styles from "./auth.module.css"
|
||||
import Link from "../../components/link"
|
||||
import { signIn } from "next-auth/react"
|
||||
|
@ -94,9 +94,7 @@ const Auth = ({
|
|||
type="password"
|
||||
id="server-password"
|
||||
value={serverPassword}
|
||||
onChange={(event) =>
|
||||
setServerPassword(event.currentTarget.value)
|
||||
}
|
||||
onChange={handleChangeServerPassword}
|
||||
placeholder="Server Password"
|
||||
required={true}
|
||||
width="100%"
|
||||
|
|
43
src/app/settings/components/sections/api-keys.module.css
Normal file
43
src/app/settings/components/sections/api-keys.module.css
Normal file
|
@ -0,0 +1,43 @@
|
|||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap);
|
||||
max-width: 300px;
|
||||
margin-top: var(--gap);
|
||||
}
|
||||
|
||||
.upload {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap);
|
||||
max-width: 300px;
|
||||
margin-top: var(--gap);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.uploadInput {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
width: 300px;
|
||||
height: 37px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.uploadButton {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* hover should affect button */
|
||||
.uploadInput:hover + button {
|
||||
border: 1px solid var(--fg);
|
||||
}
|
||||
|
||||
.tokens {
|
||||
margin-top: var(--gap);
|
||||
}
|
||||
|
||||
.tokens table thead th {
|
||||
text-align: left;
|
||||
}
|
169
src/app/settings/components/sections/api-keys.tsx
Normal file
169
src/app/settings/components/sections/api-keys.tsx
Normal file
|
@ -0,0 +1,169 @@
|
|||
"use client"
|
||||
|
||||
import Button from "@components/button"
|
||||
import Input from "@components/input"
|
||||
import Note from "@components/note"
|
||||
import { Spinner } from "@components/spinner"
|
||||
import { useToasts } from "@components/toasts"
|
||||
import { ApiToken } from "@prisma/client"
|
||||
import { useSession } from "next-auth/react"
|
||||
import { useState } from "react"
|
||||
import useSWR from "swr"
|
||||
import styles from "./api-keys.module.css"
|
||||
|
||||
type ConvertDateToString<T> = {
|
||||
[P in keyof T]: T[P] extends Date ? string : T[P]
|
||||
}
|
||||
|
||||
type SerializedApiToken = ConvertDateToString<ApiToken>
|
||||
|
||||
// need to pass in the accessToken
|
||||
const APIKeys = ({ tokens: initialTokens }: { tokens?: SerializedApiToken[] }) => {
|
||||
const session = useSession()
|
||||
const { setToast } = useToasts()
|
||||
const { data, error, mutate } = useSWR<SerializedApiToken[]>(
|
||||
"/api/user/tokens?userId=" + session?.data?.user?.id,
|
||||
{
|
||||
fetcher: async (url: string) => {
|
||||
if (session.status === "loading") return initialTokens
|
||||
|
||||
return fetch(url).then(async (res) => {
|
||||
const data = await res.json()
|
||||
if (data.error) {
|
||||
setError(data.error)
|
||||
return
|
||||
} else {
|
||||
setError(undefined)
|
||||
}
|
||||
|
||||
return data
|
||||
})
|
||||
},
|
||||
fallbackData: initialTokens
|
||||
}
|
||||
)
|
||||
|
||||
const [submitting, setSubmitting] = useState<boolean>(false)
|
||||
const [newToken, setNewToken] = useState<string>("")
|
||||
const [errorText, setError] = useState<string>()
|
||||
|
||||
const createToken = async (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault()
|
||||
if (!newToken) {
|
||||
return
|
||||
}
|
||||
setSubmitting(true)
|
||||
|
||||
const res = await fetch(
|
||||
`/api/user/tokens?userId=${session.data?.user.id}&name=${newToken}`,
|
||||
{
|
||||
method: "POST",
|
||||
}
|
||||
)
|
||||
|
||||
const response = await res.json()
|
||||
if (response.error) {
|
||||
setError(response.error)
|
||||
return
|
||||
} else {
|
||||
setError(undefined)
|
||||
}
|
||||
|
||||
setSubmitting(false)
|
||||
navigator.clipboard.writeText(response.token)
|
||||
mutate([...(data || []), response])
|
||||
setNewToken("")
|
||||
setToast({
|
||||
message: "Copied to clipboard!",
|
||||
type: "success"
|
||||
})
|
||||
}
|
||||
|
||||
const expireToken = async (id: string) => {
|
||||
setSubmitting(true)
|
||||
await fetch(`/api/user/tokens?userId=${session.data?.user.id}&tokenId=${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
Authorization: "Bearer " + session?.data?.user.sessionToken
|
||||
}
|
||||
})
|
||||
setSubmitting(false)
|
||||
mutate(data?.filter((token) => token.id !== id))
|
||||
}
|
||||
|
||||
const onChangeNewToken = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setNewToken(e.target.value)
|
||||
}
|
||||
|
||||
const hasError = Boolean(error || errorText)
|
||||
return (
|
||||
<>
|
||||
{!hasError && (
|
||||
<Note type="info">
|
||||
API keys allow you to access the API from 3rd party tools.
|
||||
</Note>
|
||||
)}
|
||||
{hasError && <Note type="error">{error?.message || errorText}</Note>}
|
||||
|
||||
<form className={styles.form}>
|
||||
<h3>Create new</h3>
|
||||
<Input
|
||||
type="text"
|
||||
value={newToken}
|
||||
onChange={onChangeNewToken}
|
||||
aria-label="API Key name"
|
||||
placeholder="Name"
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={createToken}
|
||||
loading={submitting}
|
||||
disabled={!newToken}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<div className={styles.tokens}>
|
||||
{data ? (
|
||||
data?.length ? (
|
||||
<table width={'100%'}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Expires</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data?.map((token) => (
|
||||
<tr key={token.id}>
|
||||
<td>{token.name}</td>
|
||||
<td>{new Date(token.expiresAt).toDateString()}</td>
|
||||
<td>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => expireToken(token.id)}
|
||||
loading={submitting}
|
||||
>
|
||||
Revoke
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<p>You have no API keys.</p>
|
||||
)
|
||||
) : (
|
||||
<div style={{ marginTop: "var(--gap-quarter)" }}>
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default APIKeys
|
|
@ -6,17 +6,6 @@
|
|||
margin-top: var(--gap);
|
||||
}
|
||||
|
||||
/* <div className={styles.upload}>
|
||||
<input
|
||||
type="file"
|
||||
disabled={imageViaOauth}
|
||||
className={styles.uploadInput}
|
||||
/>
|
||||
<Button type="button" disabled={imageViaOauth} width="100%" className={styles.uploadButton}>
|
||||
Upload
|
||||
</Button>
|
||||
</div> */
|
||||
/* we want the file input to be invisible and full width but still interactive button */
|
||||
.upload {
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
import SettingsGroup from "../components/settings-group"
|
||||
import Profile from "app/settings/components/sections/profile"
|
||||
import APIKeys from "./components/sections/api-keys"
|
||||
|
||||
export default async function SettingsPage() {
|
||||
return (
|
||||
<SettingsGroup title="Profile">
|
||||
<Profile />
|
||||
</SettingsGroup>
|
||||
<>
|
||||
<SettingsGroup title="Profile">
|
||||
<Profile />
|
||||
</SettingsGroup>
|
||||
<SettingsGroup title="API Keys">
|
||||
<APIKeys />
|
||||
</SettingsGroup>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -142,7 +142,6 @@ export const authOptions: NextAuthOptions = {
|
|||
events: {
|
||||
createUser: async ({ user }) => {
|
||||
const totalUsers = await prisma.user.count()
|
||||
console.log('totalUsers', totalUsers)
|
||||
if (config.enable_admin && totalUsers === 1) {
|
||||
await prisma.user.update({
|
||||
where: {
|
||||
|
@ -175,6 +174,7 @@ export const authOptions: NextAuthOptions = {
|
|||
session.user.email = token.email
|
||||
session.user.image = token.picture
|
||||
session.user.role = token.role
|
||||
session.user.sessionToken = token.sessionToken
|
||||
}
|
||||
|
||||
return session
|
||||
|
@ -208,7 +208,8 @@ export const authOptions: NextAuthOptions = {
|
|||
email: dbUser.email,
|
||||
picture: dbUser.image,
|
||||
role: dbUser.role || "user",
|
||||
username: dbUser.username
|
||||
username: dbUser.username,
|
||||
sessionToken: token.sessionToken
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ declare global {
|
|||
|
||||
import config from "@lib/config"
|
||||
import { Post, PrismaClient, User, Prisma } from "@prisma/client"
|
||||
import * as crypto from "crypto"
|
||||
export type { User, File, Post } from "@prisma/client"
|
||||
|
||||
export const prisma =
|
||||
|
@ -282,3 +283,22 @@ export const searchPosts = async (
|
|||
|
||||
return posts as ServerPostWithFiles[]
|
||||
}
|
||||
|
||||
function generateApiToken() {
|
||||
return crypto.randomBytes(32).toString("hex")
|
||||
}
|
||||
|
||||
export const createApiToken = async (userId: User["id"], name: string) => {
|
||||
const apiToken = await prisma.apiToken.create({
|
||||
data: {
|
||||
token: generateApiToken(),
|
||||
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30 * 3),
|
||||
user: {
|
||||
connect: { id: userId }
|
||||
},
|
||||
name
|
||||
}
|
||||
})
|
||||
|
||||
return apiToken
|
||||
}
|
||||
|
|
54
src/lib/server/verify-api-user.ts
Normal file
54
src/lib/server/verify-api-user.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
import { NextApiRequest, NextApiResponse } from "next"
|
||||
import { unstable_getServerSession } from "next-auth"
|
||||
import { authOptions } from "./auth"
|
||||
import { parseQueryParam } from "./parse-query-param"
|
||||
import { prisma } from "./prisma"
|
||||
|
||||
/**
|
||||
* verifyApiUser checks for a `userId` param. If it exists, it checks that the
|
||||
* user is authenticated with Next-Auth and that the user id matches the param. If the param
|
||||
* does not exist, it checks for an auth token in the request headers.
|
||||
*
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns the user id if the user is authenticated, null otherwise
|
||||
*/
|
||||
export const verifyApiUser = async (
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) => {
|
||||
const userId = parseQueryParam(req.query.userId)
|
||||
|
||||
if (!userId) {
|
||||
return parseAndCheckAuthToken(req)
|
||||
}
|
||||
|
||||
const session = await unstable_getServerSession(req, res, authOptions)
|
||||
if (!session) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (session.user.id !== userId) {
|
||||
return null
|
||||
}
|
||||
|
||||
return userId
|
||||
}
|
||||
|
||||
const parseAndCheckAuthToken = async (req: NextApiRequest) => {
|
||||
const token = req.headers.authorization?.split(" ")[1]
|
||||
if (!token) {
|
||||
return null
|
||||
}
|
||||
|
||||
const user = await prisma.apiToken.findUnique({
|
||||
where: {
|
||||
token
|
||||
},
|
||||
select: {
|
||||
userId: true
|
||||
}
|
||||
})
|
||||
|
||||
return user?.userId
|
||||
}
|
|
@ -14,9 +14,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@next-auth/prisma-adapter": "^1.0.5",
|
||||
"@next/eslint-plugin-next": "13.1.1-canary.1",
|
||||
"@next/font": "13.1.1-canary.1",
|
||||
"@prisma/client": "^4.7.1",
|
||||
"@next/eslint-plugin-next": "13.1.2-canary.0",
|
||||
"@next/font": "13.1.2-canary.0",
|
||||
"@prisma/client": "^4.8.0",
|
||||
"@radix-ui/react-dialog": "^1.0.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.1",
|
||||
"@radix-ui/react-popover": "^1.0.2",
|
||||
|
@ -28,10 +28,9 @@
|
|||
"client-zip": "2.2.1",
|
||||
"jest": "^29.3.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"next": "13.1.1-canary.1",
|
||||
"next": "13.1.2-canary.0",
|
||||
"next-auth": "^4.18.6",
|
||||
"next-themes": "^0.2.1",
|
||||
"prisma": "^4.7.1",
|
||||
"react": "18.2.0",
|
||||
"react-datepicker": "4.8.0",
|
||||
"react-dom": "18.2.0",
|
||||
|
@ -39,6 +38,7 @@
|
|||
"react-feather": "^2.0.10",
|
||||
"react-hot-toast": "2.4.0-beta.0",
|
||||
"server-only": "^0.0.1",
|
||||
"swr": "^2.0.0",
|
||||
"textarea-markdown-editor": "1.0.4",
|
||||
"ts-jest": "^29.0.3",
|
||||
"uuid": "^9.0.0"
|
||||
|
@ -61,6 +61,7 @@
|
|||
"eslint-config-next": "13.0.3",
|
||||
"next-unused": "0.0.6",
|
||||
"prettier": "2.6.2",
|
||||
"prisma": "^4.8.0",
|
||||
"typescript": "4.6.4",
|
||||
"typescript-plugin-css-modules": "3.4.0"
|
||||
},
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
import { parseQueryParam } from "@lib/server/parse-query-param"
|
||||
import { getPostsByUser } from "@lib/server/prisma"
|
||||
import { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
export default async function handle(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
switch (req.method) {
|
||||
case "GET": {
|
||||
const userId = parseQueryParam(req.query.userId)
|
||||
if (!userId) {
|
||||
return res.status(400).json({ error: "Missing userId" })
|
||||
}
|
||||
|
||||
const posts = await getPostsByUser(userId)
|
||||
return res.json(posts)
|
||||
}
|
||||
default:
|
||||
return res.status(405).end()
|
||||
}
|
||||
}
|
59
src/pages/api/user/tokens.ts
Normal file
59
src/pages/api/user/tokens.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { parseQueryParam } from "@lib/server/parse-query-param"
|
||||
import { NextApiRequest, NextApiResponse } from "next"
|
||||
import { createApiToken, prisma } from "@lib/server/prisma"
|
||||
import { verifyApiUser } from "@lib/server/verify-api-user"
|
||||
|
||||
export default async function handle(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
const userId = await verifyApiUser(req, res)
|
||||
if (!userId) {
|
||||
return res.status(400).json({ error: "Missing userId or auth token" })
|
||||
}
|
||||
|
||||
switch (req.method) {
|
||||
case "GET": {
|
||||
const tokens = await prisma.apiToken.findMany({
|
||||
where: {
|
||||
userId
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
userId: true,
|
||||
createdAt: true,
|
||||
expiresAt: true,
|
||||
name: true
|
||||
}
|
||||
});
|
||||
|
||||
console.log(tokens)
|
||||
|
||||
return res.json(tokens)
|
||||
}
|
||||
case "POST": {
|
||||
const name = parseQueryParam(req.query.name)
|
||||
if (!name) {
|
||||
return res.status(400).json({ error: "Missing token name" })
|
||||
}
|
||||
const token = await createApiToken(userId, name)
|
||||
return res.json(token)
|
||||
}
|
||||
case "DELETE": {
|
||||
const tokenId = parseQueryParam(req.query.tokenId)
|
||||
if (!tokenId) {
|
||||
return res.status(400).json({ error: "Missing tokenId" })
|
||||
}
|
||||
|
||||
await prisma.apiToken.delete({
|
||||
where: {
|
||||
id: tokenId
|
||||
}
|
||||
})
|
||||
|
||||
return res.status(204).end()
|
||||
}
|
||||
default:
|
||||
return res.status(405).end()
|
||||
}
|
||||
}
|
|
@ -3,9 +3,9 @@ lockfileVersion: 5.4
|
|||
specifiers:
|
||||
'@next-auth/prisma-adapter': ^1.0.5
|
||||
'@next/bundle-analyzer': 13.0.7-canary.4
|
||||
'@next/eslint-plugin-next': 13.1.1-canary.1
|
||||
'@next/font': 13.1.1-canary.1
|
||||
'@prisma/client': ^4.7.1
|
||||
'@next/eslint-plugin-next': 13.1.2-canary.0
|
||||
'@next/font': 13.1.2-canary.0
|
||||
'@prisma/client': ^4.8.0
|
||||
'@radix-ui/react-dialog': ^1.0.2
|
||||
'@radix-ui/react-dropdown-menu': ^2.0.1
|
||||
'@radix-ui/react-popover': ^1.0.2
|
||||
|
@ -31,12 +31,12 @@ specifiers:
|
|||
eslint-config-next: 13.0.3
|
||||
jest: ^29.3.1
|
||||
lodash.debounce: ^4.0.8
|
||||
next: 13.1.1-canary.1
|
||||
next: 13.1.2-canary.0
|
||||
next-auth: ^4.18.6
|
||||
next-themes: ^0.2.1
|
||||
next-unused: 0.0.6
|
||||
prettier: 2.6.2
|
||||
prisma: ^4.7.1
|
||||
prisma: ^4.8.0
|
||||
react: 18.2.0
|
||||
react-datepicker: 4.8.0
|
||||
react-dom: 18.2.0
|
||||
|
@ -45,6 +45,7 @@ specifiers:
|
|||
react-hot-toast: 2.4.0-beta.0
|
||||
server-only: ^0.0.1
|
||||
sharp: ^0.31.2
|
||||
swr: ^2.0.0
|
||||
textarea-markdown-editor: 1.0.4
|
||||
ts-jest: ^29.0.3
|
||||
typescript: 4.6.4
|
||||
|
@ -52,25 +53,24 @@ specifiers:
|
|||
uuid: ^9.0.0
|
||||
|
||||
dependencies:
|
||||
'@next-auth/prisma-adapter': 1.0.5_64qbzg5ec56bux2misz3l4u6g4
|
||||
'@next/eslint-plugin-next': 13.1.1-canary.1
|
||||
'@next/font': 13.1.1-canary.1
|
||||
'@prisma/client': 4.7.1_prisma@4.7.1
|
||||
'@next-auth/prisma-adapter': 1.0.5_fmf72d7n4jt7coiyftaa4dlrhe
|
||||
'@next/eslint-plugin-next': 13.1.2-canary.0
|
||||
'@next/font': 13.1.2-canary.0
|
||||
'@prisma/client': 4.8.0_prisma@4.8.0
|
||||
'@radix-ui/react-dialog': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
||||
'@radix-ui/react-dropdown-menu': 2.0.1_jbvntnid6ohjelon6ccj5dhg2u
|
||||
'@radix-ui/react-popover': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
||||
'@radix-ui/react-tabs': 1.0.1_biqbaboplfbrettd7655fr4n2y
|
||||
'@radix-ui/react-tooltip': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
||||
'@wcj/markdown-to-html': 2.1.2
|
||||
'@wits/next-themes': 0.2.14_jcrpix7mbfpfu5movksylxa5c4
|
||||
'@wits/next-themes': 0.2.14_3tcywg5dy5qhcmsv6sy2pt6lua
|
||||
client-only: 0.0.1
|
||||
client-zip: 2.2.1
|
||||
jest: 29.3.1_@types+node@17.0.23
|
||||
lodash.debounce: 4.0.8
|
||||
next: 13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y
|
||||
next-auth: 4.18.6_jcrpix7mbfpfu5movksylxa5c4
|
||||
next-themes: 0.2.1_jcrpix7mbfpfu5movksylxa5c4
|
||||
prisma: 4.7.1
|
||||
next: 13.1.2-canary.0_biqbaboplfbrettd7655fr4n2y
|
||||
next-auth: 4.18.6_3tcywg5dy5qhcmsv6sy2pt6lua
|
||||
next-themes: 0.2.1_3tcywg5dy5qhcmsv6sy2pt6lua
|
||||
react: 18.2.0
|
||||
react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
|
@ -78,6 +78,7 @@ dependencies:
|
|||
react-feather: 2.0.10_react@18.2.0
|
||||
react-hot-toast: 2.4.0-beta.0_owo25xnefcwdq3zjgtohz6dbju
|
||||
server-only: 0.0.1
|
||||
swr: 2.0.0_react@18.2.0
|
||||
textarea-markdown-editor: 1.0.4_biqbaboplfbrettd7655fr4n2y
|
||||
ts-jest: 29.0.3_7hcmezpa7bajbjecov7p46z4aa
|
||||
uuid: 9.0.0
|
||||
|
@ -103,6 +104,7 @@ devDependencies:
|
|||
eslint-config-next: 13.0.3_hsmo2rtalirsvadpuxki35bq2i
|
||||
next-unused: 0.0.6
|
||||
prettier: 2.6.2
|
||||
prisma: 4.8.0
|
||||
typescript: 4.6.4
|
||||
typescript-plugin-css-modules: 3.4.0_typescript@4.6.4
|
||||
|
||||
|
@ -785,14 +787,14 @@ packages:
|
|||
'@jridgewell/sourcemap-codec': 1.4.14
|
||||
dev: false
|
||||
|
||||
/@next-auth/prisma-adapter/1.0.5_64qbzg5ec56bux2misz3l4u6g4:
|
||||
/@next-auth/prisma-adapter/1.0.5_fmf72d7n4jt7coiyftaa4dlrhe:
|
||||
resolution: {integrity: sha512-VqMS11IxPXrPGXw6Oul6jcyS/n8GLOWzRMrPr3EMdtD6eOalM6zz05j08PcNiis8QzkfuYnCv49OvufTuaEwYQ==}
|
||||
peerDependencies:
|
||||
'@prisma/client': '>=2.26.0 || >=3'
|
||||
next-auth: ^4
|
||||
dependencies:
|
||||
'@prisma/client': 4.7.1_prisma@4.7.1
|
||||
next-auth: 4.18.6_jcrpix7mbfpfu5movksylxa5c4
|
||||
'@prisma/client': 4.8.0_prisma@4.8.0
|
||||
next-auth: 4.18.6_3tcywg5dy5qhcmsv6sy2pt6lua
|
||||
dev: false
|
||||
|
||||
/@next/bundle-analyzer/13.0.7-canary.4:
|
||||
|
@ -804,8 +806,8 @@ packages:
|
|||
- utf-8-validate
|
||||
dev: true
|
||||
|
||||
/@next/env/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-h8DEj69dLJpFUuXOVPQeJ4/X1LbW5mtZSsaS5Xr/pt2VbrRN50eAV/6rMY+l6U6p/4AX1/F5aK4UBzLQJbwFzw==}
|
||||
/@next/env/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-8IwZJ557A7L81FsDGH64/u1oHgBGNW1zlpVGukFXUPh5XF3j/OMtaPR+gxGa59OTDuWMiydKrikX+1Obv7xwcg==}
|
||||
dev: false
|
||||
|
||||
/@next/eslint-plugin-next/13.0.3:
|
||||
|
@ -814,18 +816,18 @@ packages:
|
|||
glob: 7.1.7
|
||||
dev: true
|
||||
|
||||
/@next/eslint-plugin-next/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-gKabWQJ+Aps/u/lzVC3FoGwbG+r1t3cwoUXPbcpC3igrpBSbkhEoK9u3MYeMlkAUywJ7IvsENWVYqjzRuaso4Q==}
|
||||
/@next/eslint-plugin-next/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-EeH9njTYTmaM814ByCF9B+KPR3tEYYtrGoqrNLGd6asMNRG6q49dgsE+JxdZ+vYws6NItHqL645W6bqANK0Vhg==}
|
||||
dependencies:
|
||||
glob: 7.1.7
|
||||
dev: false
|
||||
|
||||
/@next/font/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-cygeAS0h5OuWaorcQ6Ry8c/E0fwZEGQfZy7kUjXHVn6DP4sekB2RgR9aNWL3cUoJ35RwjwtsR7K6FufUyzfGag==}
|
||||
/@next/font/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-HfutDb/73yxmqO7UOsh1It2jLtKCJq6meEZxIwSulikWCqRukxs25Li3tUq+r6eDXkU2y6cfVv1MxNc6I3lTBA==}
|
||||
dev: false
|
||||
|
||||
/@next/swc-android-arm-eabi/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-0McGEjTnNXdBTlghWxkuM07qpKMr44afLeGFpS/zwIlDV7lNOXFzCpyHdJoJsFL4kBJgfbyCi8aamnhwqlwZxA==}
|
||||
/@next/swc-android-arm-eabi/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-h30pxaiAUiZqkYDlcLTIVkXswPB0U3zubjel4ejJ2/SszbQrd9JTfj20Kq25IPjKIYOll8kRr/pIX4iukuGGmw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
@ -833,8 +835,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-android-arm64/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-XCmPqmhtsc52lv0Qs/maThRrQpHMRK1AqFhgFXfFG9wclbFBtQIUapD/qD7nOlXbch+7RDONbABPf/8pE2T0cQ==}
|
||||
/@next/swc-android-arm64/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-QNdrO5rFxqHaNBUHYKfo8iGI9VYkyNhxTF/66JVv23IxKneYzS2QyuFH3S2xCxTGtxsQVPmRf1W9UA4dfjU70A==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
@ -842,8 +844,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-darwin-arm64/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-qz+et20cTetOppH6stlDW171tTo1vG4eHGmXY1Zwa3D/sZPk5IRsqsmpdzxuBsVxdk5x7zaliYZowOlQM2awnw==}
|
||||
/@next/swc-darwin-arm64/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-cJfA2blHqFAWvduKSGNxSE5ttowjIY4yYaGx+cbvq5j48D8Fe8QhWgTLwMVeIE7PA+kUoIsZrxLRoJyNFry6bQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
@ -851,8 +853,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-darwin-x64/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-rPGOUsxturFtSkqtbSis1kBuu0mNzCPibWEMihsM32EzdXeDXJMgl5EP3+RiwGfrawON5lcTEz0r52Zll+0kmw==}
|
||||
/@next/swc-darwin-x64/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-9loctTiLf7GyuTAuujH/OOabdAElHoVthpt9ELtgVByE2aUpQJpBdgeVc5SWDozlrxKnuDV+TvFOJaodLiF5BA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
@ -860,8 +862,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-freebsd-x64/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-tEnpdXSEzltEEbeh32w4eQN1znR35xjX0pMC7leud8XhJvppWwdEqfdOp3OuviPmb8p6LzFqYyknNe710cFy+Q==}
|
||||
/@next/swc-freebsd-x64/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-ptz2x8HnT8VeFNlZXSF5J2IbaLDZFzGfWcbSvU3PiOCZPMje4ROdnUxTM4yLSFTrw1uA47a8rWlBQxfrO1c/eA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
@ -869,8 +871,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm-gnueabihf/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-EJdCFjRHVoyDC8Q0N8ULCJ7+5hl4NhaELlu+04cCcgQ3qFZkFZIfTLrXnCT1aa2Y8yyR5FvyBeHgvusL5abqpQ==}
|
||||
/@next/swc-linux-arm-gnueabihf/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-lzJRomdDC4qZ8W1kmZ24SvyQG2Q8RuAe4F00x4TEuecy4kt+PfDmRRmHpNMr9JIl4/KfYhFSK730dfsVos/f9g==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
@ -878,8 +880,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm64-gnu/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-BRN7Beg1OASa2F7FGYAdYL3O+bA2wFX6ow9QnHD312+JHCf/IKun3FSxSXBaSnc8ZJCnexmSWIz+hewKN1jGQQ==}
|
||||
/@next/swc-linux-arm64-gnu/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-EtigR57JBv2l5oHf8FJGUrVfe+Lx60gKrcosnoMoIf9YO9HhSfmbiaueO9heOegSBQVBsbjBhx807WTVqRTT+Q==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
@ -887,8 +889,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm64-musl/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-WE1muJmocpSHUHBH02iMOy9RR4Hz7XFM6tjAevY40svXNmGNszhYzsm0MQ+/VnlqP9f9l1/dEiPN6tSbMAlH9A==}
|
||||
/@next/swc-linux-arm64-musl/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-9snjPWQ2JdF8hRzwyOLzHLDPGPkysGRyl34uvG0YQ4/I6Hsk/zVdG7gSVtODMzyNKs6oQTA036SjDq9vGyzpPQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
@ -896,8 +898,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-x64-gnu/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-aeBiutM8gFndpUkDA6t8DKzD9TcYg48+b7QxuL2XyRJo+47muhNbXaB6y/MwarxwjnsAry0hMs/ycP3lOL7vnw==}
|
||||
/@next/swc-linux-x64-gnu/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-jQyRP7oQwK4EDH8pPZSNS1K94azTDHvGF2fA6QQwxntSB6ek3CS9EGHMzNquV3gsDLtBFP2LdWiQJ3+uWT4qyw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
@ -905,8 +907,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-x64-musl/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-JyJzejDuu68bZj1jrdbgJEIyj0xQy8N0R363T6Rx5/F5Htk2vVzXaP+MkANcWuZjvmH/BHjQc515liiTwQ328Q==}
|
||||
/@next/swc-linux-x64-musl/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-VQHhBjbGXZEAWLP9DG1O0ZG4GltG04oG3TzDjY+NllcAnSiwQ/KwZgwmbAqQqvbYqTEbMmz45YNUqB9MiyMZZg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
@ -914,8 +916,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-arm64-msvc/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-y/VxMhjXrTt4fGzrJwdfa6MM2ZauZ0dX20aRGDX/6VeaxO5toBsmXF7cwoDC97C65l93FY/X9vyc75WSLrXFrA==}
|
||||
/@next/swc-win32-arm64-msvc/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-YOkIOf5iJB32ouK+XJB+yuAKwzXH2r5pXpvJAHH8105tI7B6Bj8GkaUjoMsBb6qSFudE2Axn38r8XRmec4y7Uw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
@ -923,8 +925,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-ia32-msvc/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-Nk1DdvC+Ocdqnj4Ra+qWJK/PQ68hrWmSg3FXL4I3pooX2IZcUSF8nPFNS0r8V47inTAXbwatcFEKSBRjFBS2ww==}
|
||||
/@next/swc-win32-ia32-msvc/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-wOU1b+8Qma47i5CNlZBparatFxIyEdNTXmjv/ce/C/O34i02uYTBo9iNId8K6OCJ/T9W1o6fRJItHoDKBUg3cg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
@ -932,8 +934,8 @@ packages:
|
|||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-x64-msvc/13.1.1-canary.1:
|
||||
resolution: {integrity: sha512-/7q6tjUebSaUYTGZRpp4qAmrcL6+tiKfHN5YgW6zpX5MWLEk1DkdnuBjO/jSvCJd0510byBkN6drlzmfTMjzzg==}
|
||||
/@next/swc-win32-x64-msvc/13.1.2-canary.0:
|
||||
resolution: {integrity: sha512-E/YYo47qCRFIat2G8doU5M09We10xq9K3hPukZHqouwk7gQBj7HUhT5p08FtXytqOljzBzNv5E7ba3aicub1Rw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
@ -973,8 +975,8 @@ packages:
|
|||
/@popperjs/core/2.11.6:
|
||||
resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
|
||||
|
||||
/@prisma/client/4.7.1_prisma@4.7.1:
|
||||
resolution: {integrity: sha512-/GbnOwIPtjiveZNUzGXOdp7RxTEkHL4DZP3vBaFNadfr6Sf0RshU5EULFzVaSi9i9PIK9PYd+1Rn7z2B2npb9w==}
|
||||
/@prisma/client/4.8.0_prisma@4.8.0:
|
||||
resolution: {integrity: sha512-Y1riB0p2W52kh3zgssP/YAhln3RjBFcJy3uwEiyjmU+TQYh6QTZDRFBo3JtBWuq2FyMOl1Rye8jxzUP+n0l5Cg==}
|
||||
engines: {node: '>=14.17'}
|
||||
requiresBuild: true
|
||||
peerDependencies:
|
||||
|
@ -983,18 +985,17 @@ packages:
|
|||
prisma:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@prisma/engines-version': 4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c
|
||||
prisma: 4.7.1
|
||||
'@prisma/engines-version': 4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe
|
||||
prisma: 4.8.0
|
||||
dev: false
|
||||
|
||||
/@prisma/engines-version/4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c:
|
||||
resolution: {integrity: sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==}
|
||||
/@prisma/engines-version/4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe:
|
||||
resolution: {integrity: sha512-MHSOSexomRMom8QN4t7bu87wPPD+pa+hW9+71JnVcF3DqyyO/ycCLhRL1we3EojRpZxKvuyGho2REQsMCvxcJw==}
|
||||
dev: false
|
||||
|
||||
/@prisma/engines/4.7.1:
|
||||
resolution: {integrity: sha512-zWabHosTdLpXXlMefHmnouhXMoTB1+SCbUU3t4FCmdrtIOZcarPKU3Alto7gm/pZ9vHlGOXHCfVZ1G7OIrSbog==}
|
||||
/@prisma/engines/4.8.0:
|
||||
resolution: {integrity: sha512-A1Asn2rxZMlLAj1HTyfaCv0VQrLUv034jVay05QlqZg1qiHPeA3/pGTfNMijbsMYCsGVxfWEJuaZZuNxXGMCrA==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/@radix-ui/primitive/1.0.0:
|
||||
resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==}
|
||||
|
@ -1804,14 +1805,14 @@ packages:
|
|||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@wits/next-themes/0.2.14_jcrpix7mbfpfu5movksylxa5c4:
|
||||
/@wits/next-themes/0.2.14_3tcywg5dy5qhcmsv6sy2pt6lua:
|
||||
resolution: {integrity: sha512-fHKb/tRcWbYNblGHZtfvAQztDhzUB9d7ZkYOny0BisSPh6EABcsqxKB48ABUQztcmKywlp2zEMkLcSRj/PQBSw==}
|
||||
peerDependencies:
|
||||
next: '*'
|
||||
react: '*'
|
||||
react-dom: '*'
|
||||
dependencies:
|
||||
next: 13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y
|
||||
next: 13.1.2-canary.0_biqbaboplfbrettd7655fr4n2y
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
dev: false
|
||||
|
@ -5217,7 +5218,7 @@ packages:
|
|||
dev: true
|
||||
optional: true
|
||||
|
||||
/next-auth/4.18.6_jcrpix7mbfpfu5movksylxa5c4:
|
||||
/next-auth/4.18.6_3tcywg5dy5qhcmsv6sy2pt6lua:
|
||||
resolution: {integrity: sha512-0TQwbq5X9Jyd1wUVYUoyvHJh4JWXeW9UOcMEl245Er/Y5vsSbyGJHt8M7xjRMzk9mORVMYehoMdERgyiq/jCgA==}
|
||||
engines: {node: ^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0}
|
||||
peerDependencies:
|
||||
|
@ -5233,7 +5234,7 @@ packages:
|
|||
'@panva/hkdf': 1.0.2
|
||||
cookie: 0.5.0
|
||||
jose: 4.11.0
|
||||
next: 13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y
|
||||
next: 13.1.2-canary.0_biqbaboplfbrettd7655fr4n2y
|
||||
oauth: 0.9.15
|
||||
openid-client: 5.3.0
|
||||
preact: 10.11.2
|
||||
|
@ -5243,14 +5244,14 @@ packages:
|
|||
uuid: 8.3.2
|
||||
dev: false
|
||||
|
||||
/next-themes/0.2.1_jcrpix7mbfpfu5movksylxa5c4:
|
||||
/next-themes/0.2.1_3tcywg5dy5qhcmsv6sy2pt6lua:
|
||||
resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
|
||||
peerDependencies:
|
||||
next: '*'
|
||||
react: '*'
|
||||
react-dom: '*'
|
||||
dependencies:
|
||||
next: 13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y
|
||||
next: 13.1.2-canary.0_biqbaboplfbrettd7655fr4n2y
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
dev: false
|
||||
|
@ -5266,8 +5267,8 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/next/13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-20EeQyfGs9dGUAPrXAod5jay1plcM0itItL/7z9BMczYM55/it8TxS1OPTmseyM9Y8uuybTRoCHeKh6TCI09tg==}
|
||||
/next/13.1.2-canary.0_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-EUzXiMtS6tTyqyZnD3WtDPPO8K8lMNfuPd7NaxpDCEBJZ4YbeffiMpco9m5aIJpgt6uQ2TdGOuYl9zKj6WF2gw==}
|
||||
engines: {node: '>=14.6.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
|
@ -5284,7 +5285,7 @@ packages:
|
|||
sass:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@next/env': 13.1.1-canary.1
|
||||
'@next/env': 13.1.2-canary.0
|
||||
'@swc/helpers': 0.4.14
|
||||
caniuse-lite: 1.0.30001431
|
||||
postcss: 8.4.14
|
||||
|
@ -5292,19 +5293,19 @@ packages:
|
|||
react-dom: 18.2.0_react@18.2.0
|
||||
styled-jsx: 5.1.1_react@18.2.0
|
||||
optionalDependencies:
|
||||
'@next/swc-android-arm-eabi': 13.1.1-canary.1
|
||||
'@next/swc-android-arm64': 13.1.1-canary.1
|
||||
'@next/swc-darwin-arm64': 13.1.1-canary.1
|
||||
'@next/swc-darwin-x64': 13.1.1-canary.1
|
||||
'@next/swc-freebsd-x64': 13.1.1-canary.1
|
||||
'@next/swc-linux-arm-gnueabihf': 13.1.1-canary.1
|
||||
'@next/swc-linux-arm64-gnu': 13.1.1-canary.1
|
||||
'@next/swc-linux-arm64-musl': 13.1.1-canary.1
|
||||
'@next/swc-linux-x64-gnu': 13.1.1-canary.1
|
||||
'@next/swc-linux-x64-musl': 13.1.1-canary.1
|
||||
'@next/swc-win32-arm64-msvc': 13.1.1-canary.1
|
||||
'@next/swc-win32-ia32-msvc': 13.1.1-canary.1
|
||||
'@next/swc-win32-x64-msvc': 13.1.1-canary.1
|
||||
'@next/swc-android-arm-eabi': 13.1.2-canary.0
|
||||
'@next/swc-android-arm64': 13.1.2-canary.0
|
||||
'@next/swc-darwin-arm64': 13.1.2-canary.0
|
||||
'@next/swc-darwin-x64': 13.1.2-canary.0
|
||||
'@next/swc-freebsd-x64': 13.1.2-canary.0
|
||||
'@next/swc-linux-arm-gnueabihf': 13.1.2-canary.0
|
||||
'@next/swc-linux-arm64-gnu': 13.1.2-canary.0
|
||||
'@next/swc-linux-arm64-musl': 13.1.2-canary.0
|
||||
'@next/swc-linux-x64-gnu': 13.1.2-canary.0
|
||||
'@next/swc-linux-x64-musl': 13.1.2-canary.0
|
||||
'@next/swc-win32-arm64-msvc': 13.1.2-canary.0
|
||||
'@next/swc-win32-ia32-msvc': 13.1.2-canary.0
|
||||
'@next/swc-win32-x64-msvc': 13.1.2-canary.0
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- babel-plugin-macros
|
||||
|
@ -5818,14 +5819,13 @@ packages:
|
|||
parse-ms: 2.1.0
|
||||
dev: true
|
||||
|
||||
/prisma/4.7.1:
|
||||
resolution: {integrity: sha512-CCQP+m+1qZOGIZlvnL6T3ZwaU0LAleIHYFPN9tFSzjs/KL6vH9rlYbGOkTuG9Q1s6Ki5D0LJlYlW18Z9EBUpGg==}
|
||||
/prisma/4.8.0:
|
||||
resolution: {integrity: sha512-DWIhxvxt8f4h6MDd35mz7BJff+fu7HItW3WPDIEpCR3RzcOWyiHBbLQW5/DOgmf+pRLTjwXQob7kuTZVYUAw5w==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@prisma/engines': 4.7.1
|
||||
dev: false
|
||||
'@prisma/engines': 4.8.0
|
||||
|
||||
/process-nextick-args/2.0.1:
|
||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||
|
@ -6670,6 +6670,16 @@ packages:
|
|||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
/swr/2.0.0_react@18.2.0:
|
||||
resolution: {integrity: sha512-IhUx5yPkX+Fut3h0SqZycnaNLXLXsb2ECFq0Y29cxnK7d8r7auY2JWNbCW3IX+EqXUg3rwNJFlhrw5Ye/b6k7w==}
|
||||
engines: {pnpm: '7'}
|
||||
peerDependencies:
|
||||
react: ^16.11.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
use-sync-external-store: 1.2.0_react@18.2.0
|
||||
dev: false
|
||||
|
||||
/tapable/1.1.3:
|
||||
resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==}
|
||||
engines: {node: '>=6'}
|
||||
|
@ -7066,6 +7076,14 @@ packages:
|
|||
tslib: 2.4.1
|
||||
dev: false
|
||||
|
||||
/use-sync-external-store/1.2.0_react@18.2.0:
|
||||
resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/util-deprecate/1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
|
|
|
@ -96,6 +96,7 @@ model User {
|
|||
posts Post[]
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
apiTokens ApiToken[]
|
||||
// below are added for CredentialProvider
|
||||
username String? @unique
|
||||
password String? @map("hashed_password")
|
||||
|
@ -111,3 +112,16 @@ model VerificationToken {
|
|||
@@unique([identifier, token])
|
||||
@@map("verification_tokens")
|
||||
}
|
||||
|
||||
model ApiToken {
|
||||
id String @default(cuid()) @id
|
||||
name String
|
||||
token String @unique
|
||||
expiresAt DateTime
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([userId])
|
||||
}
|
||||
|
|
9
src/types/next-auth.d.ts
vendored
9
src/types/next-auth.d.ts
vendored
|
@ -1,5 +1,7 @@
|
|||
import { User } from "next-auth"
|
||||
import { JWT } from "next-auth/jwt"
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import type { User } from "next-auth"
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import type { JWT } from "next-auth/jwt"
|
||||
|
||||
type UserId = string
|
||||
|
||||
|
@ -7,6 +9,7 @@ declare module "next-auth/jwt" {
|
|||
interface JWT {
|
||||
id: UserId
|
||||
role: string
|
||||
sessionToken: string
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +18,7 @@ declare module "next-auth" {
|
|||
user: User & {
|
||||
id: UserId
|
||||
role: string
|
||||
sessionToken: string
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,5 +28,6 @@ declare module "next-auth" {
|
|||
email?: string | null
|
||||
role?: string | null
|
||||
id: UserId
|
||||
token?: string
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue