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"
|
"use client"
|
||||||
|
|
||||||
import { startTransition, useEffect, useRef, useState } from "react"
|
import { startTransition, useEffect, useState } from "react"
|
||||||
import styles from "./auth.module.css"
|
import styles from "./auth.module.css"
|
||||||
import Link from "../../components/link"
|
import Link from "../../components/link"
|
||||||
import { signIn } from "next-auth/react"
|
import { signIn } from "next-auth/react"
|
||||||
|
@ -94,9 +94,7 @@ const Auth = ({
|
||||||
type="password"
|
type="password"
|
||||||
id="server-password"
|
id="server-password"
|
||||||
value={serverPassword}
|
value={serverPassword}
|
||||||
onChange={(event) =>
|
onChange={handleChangeServerPassword}
|
||||||
setServerPassword(event.currentTarget.value)
|
|
||||||
}
|
|
||||||
placeholder="Server Password"
|
placeholder="Server Password"
|
||||||
required={true}
|
required={true}
|
||||||
width="100%"
|
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);
|
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 {
|
.upload {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import SettingsGroup from "../components/settings-group"
|
import SettingsGroup from "../components/settings-group"
|
||||||
import Profile from "app/settings/components/sections/profile"
|
import Profile from "app/settings/components/sections/profile"
|
||||||
|
import APIKeys from "./components/sections/api-keys"
|
||||||
|
|
||||||
export default async function SettingsPage() {
|
export default async function SettingsPage() {
|
||||||
return (
|
return (
|
||||||
<SettingsGroup title="Profile">
|
<>
|
||||||
<Profile />
|
<SettingsGroup title="Profile">
|
||||||
</SettingsGroup>
|
<Profile />
|
||||||
|
</SettingsGroup>
|
||||||
|
<SettingsGroup title="API Keys">
|
||||||
|
<APIKeys />
|
||||||
|
</SettingsGroup>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,7 +142,6 @@ export const authOptions: NextAuthOptions = {
|
||||||
events: {
|
events: {
|
||||||
createUser: async ({ user }) => {
|
createUser: async ({ user }) => {
|
||||||
const totalUsers = await prisma.user.count()
|
const totalUsers = await prisma.user.count()
|
||||||
console.log('totalUsers', totalUsers)
|
|
||||||
if (config.enable_admin && totalUsers === 1) {
|
if (config.enable_admin && totalUsers === 1) {
|
||||||
await prisma.user.update({
|
await prisma.user.update({
|
||||||
where: {
|
where: {
|
||||||
|
@ -175,6 +174,7 @@ export const authOptions: NextAuthOptions = {
|
||||||
session.user.email = token.email
|
session.user.email = token.email
|
||||||
session.user.image = token.picture
|
session.user.image = token.picture
|
||||||
session.user.role = token.role
|
session.user.role = token.role
|
||||||
|
session.user.sessionToken = token.sessionToken
|
||||||
}
|
}
|
||||||
|
|
||||||
return session
|
return session
|
||||||
|
@ -208,7 +208,8 @@ export const authOptions: NextAuthOptions = {
|
||||||
email: dbUser.email,
|
email: dbUser.email,
|
||||||
picture: dbUser.image,
|
picture: dbUser.image,
|
||||||
role: dbUser.role || "user",
|
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 config from "@lib/config"
|
||||||
import { Post, PrismaClient, User, Prisma } from "@prisma/client"
|
import { Post, PrismaClient, User, Prisma } from "@prisma/client"
|
||||||
|
import * as crypto from "crypto"
|
||||||
export type { User, File, Post } from "@prisma/client"
|
export type { User, File, Post } from "@prisma/client"
|
||||||
|
|
||||||
export const prisma =
|
export const prisma =
|
||||||
|
@ -282,3 +283,22 @@ export const searchPosts = async (
|
||||||
|
|
||||||
return posts as ServerPostWithFiles[]
|
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": {
|
"dependencies": {
|
||||||
"@next-auth/prisma-adapter": "^1.0.5",
|
"@next-auth/prisma-adapter": "^1.0.5",
|
||||||
"@next/eslint-plugin-next": "13.1.1-canary.1",
|
"@next/eslint-plugin-next": "13.1.2-canary.0",
|
||||||
"@next/font": "13.1.1-canary.1",
|
"@next/font": "13.1.2-canary.0",
|
||||||
"@prisma/client": "^4.7.1",
|
"@prisma/client": "^4.8.0",
|
||||||
"@radix-ui/react-dialog": "^1.0.2",
|
"@radix-ui/react-dialog": "^1.0.2",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.1",
|
"@radix-ui/react-dropdown-menu": "^2.0.1",
|
||||||
"@radix-ui/react-popover": "^1.0.2",
|
"@radix-ui/react-popover": "^1.0.2",
|
||||||
|
@ -28,10 +28,9 @@
|
||||||
"client-zip": "2.2.1",
|
"client-zip": "2.2.1",
|
||||||
"jest": "^29.3.1",
|
"jest": "^29.3.1",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"next": "13.1.1-canary.1",
|
"next": "13.1.2-canary.0",
|
||||||
"next-auth": "^4.18.6",
|
"next-auth": "^4.18.6",
|
||||||
"next-themes": "^0.2.1",
|
"next-themes": "^0.2.1",
|
||||||
"prisma": "^4.7.1",
|
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-datepicker": "4.8.0",
|
"react-datepicker": "4.8.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
@ -39,6 +38,7 @@
|
||||||
"react-feather": "^2.0.10",
|
"react-feather": "^2.0.10",
|
||||||
"react-hot-toast": "2.4.0-beta.0",
|
"react-hot-toast": "2.4.0-beta.0",
|
||||||
"server-only": "^0.0.1",
|
"server-only": "^0.0.1",
|
||||||
|
"swr": "^2.0.0",
|
||||||
"textarea-markdown-editor": "1.0.4",
|
"textarea-markdown-editor": "1.0.4",
|
||||||
"ts-jest": "^29.0.3",
|
"ts-jest": "^29.0.3",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
|
@ -61,6 +61,7 @@
|
||||||
"eslint-config-next": "13.0.3",
|
"eslint-config-next": "13.0.3",
|
||||||
"next-unused": "0.0.6",
|
"next-unused": "0.0.6",
|
||||||
"prettier": "2.6.2",
|
"prettier": "2.6.2",
|
||||||
|
"prisma": "^4.8.0",
|
||||||
"typescript": "4.6.4",
|
"typescript": "4.6.4",
|
||||||
"typescript-plugin-css-modules": "3.4.0"
|
"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:
|
specifiers:
|
||||||
'@next-auth/prisma-adapter': ^1.0.5
|
'@next-auth/prisma-adapter': ^1.0.5
|
||||||
'@next/bundle-analyzer': 13.0.7-canary.4
|
'@next/bundle-analyzer': 13.0.7-canary.4
|
||||||
'@next/eslint-plugin-next': 13.1.1-canary.1
|
'@next/eslint-plugin-next': 13.1.2-canary.0
|
||||||
'@next/font': 13.1.1-canary.1
|
'@next/font': 13.1.2-canary.0
|
||||||
'@prisma/client': ^4.7.1
|
'@prisma/client': ^4.8.0
|
||||||
'@radix-ui/react-dialog': ^1.0.2
|
'@radix-ui/react-dialog': ^1.0.2
|
||||||
'@radix-ui/react-dropdown-menu': ^2.0.1
|
'@radix-ui/react-dropdown-menu': ^2.0.1
|
||||||
'@radix-ui/react-popover': ^1.0.2
|
'@radix-ui/react-popover': ^1.0.2
|
||||||
|
@ -31,12 +31,12 @@ specifiers:
|
||||||
eslint-config-next: 13.0.3
|
eslint-config-next: 13.0.3
|
||||||
jest: ^29.3.1
|
jest: ^29.3.1
|
||||||
lodash.debounce: ^4.0.8
|
lodash.debounce: ^4.0.8
|
||||||
next: 13.1.1-canary.1
|
next: 13.1.2-canary.0
|
||||||
next-auth: ^4.18.6
|
next-auth: ^4.18.6
|
||||||
next-themes: ^0.2.1
|
next-themes: ^0.2.1
|
||||||
next-unused: 0.0.6
|
next-unused: 0.0.6
|
||||||
prettier: 2.6.2
|
prettier: 2.6.2
|
||||||
prisma: ^4.7.1
|
prisma: ^4.8.0
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-datepicker: 4.8.0
|
react-datepicker: 4.8.0
|
||||||
react-dom: 18.2.0
|
react-dom: 18.2.0
|
||||||
|
@ -45,6 +45,7 @@ specifiers:
|
||||||
react-hot-toast: 2.4.0-beta.0
|
react-hot-toast: 2.4.0-beta.0
|
||||||
server-only: ^0.0.1
|
server-only: ^0.0.1
|
||||||
sharp: ^0.31.2
|
sharp: ^0.31.2
|
||||||
|
swr: ^2.0.0
|
||||||
textarea-markdown-editor: 1.0.4
|
textarea-markdown-editor: 1.0.4
|
||||||
ts-jest: ^29.0.3
|
ts-jest: ^29.0.3
|
||||||
typescript: 4.6.4
|
typescript: 4.6.4
|
||||||
|
@ -52,25 +53,24 @@ specifiers:
|
||||||
uuid: ^9.0.0
|
uuid: ^9.0.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next-auth/prisma-adapter': 1.0.5_64qbzg5ec56bux2misz3l4u6g4
|
'@next-auth/prisma-adapter': 1.0.5_fmf72d7n4jt7coiyftaa4dlrhe
|
||||||
'@next/eslint-plugin-next': 13.1.1-canary.1
|
'@next/eslint-plugin-next': 13.1.2-canary.0
|
||||||
'@next/font': 13.1.1-canary.1
|
'@next/font': 13.1.2-canary.0
|
||||||
'@prisma/client': 4.7.1_prisma@4.7.1
|
'@prisma/client': 4.8.0_prisma@4.8.0
|
||||||
'@radix-ui/react-dialog': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
'@radix-ui/react-dialog': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
||||||
'@radix-ui/react-dropdown-menu': 2.0.1_jbvntnid6ohjelon6ccj5dhg2u
|
'@radix-ui/react-dropdown-menu': 2.0.1_jbvntnid6ohjelon6ccj5dhg2u
|
||||||
'@radix-ui/react-popover': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
'@radix-ui/react-popover': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
||||||
'@radix-ui/react-tabs': 1.0.1_biqbaboplfbrettd7655fr4n2y
|
'@radix-ui/react-tabs': 1.0.1_biqbaboplfbrettd7655fr4n2y
|
||||||
'@radix-ui/react-tooltip': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
'@radix-ui/react-tooltip': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
||||||
'@wcj/markdown-to-html': 2.1.2
|
'@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-only: 0.0.1
|
||||||
client-zip: 2.2.1
|
client-zip: 2.2.1
|
||||||
jest: 29.3.1_@types+node@17.0.23
|
jest: 29.3.1_@types+node@17.0.23
|
||||||
lodash.debounce: 4.0.8
|
lodash.debounce: 4.0.8
|
||||||
next: 13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y
|
next: 13.1.2-canary.0_biqbaboplfbrettd7655fr4n2y
|
||||||
next-auth: 4.18.6_jcrpix7mbfpfu5movksylxa5c4
|
next-auth: 4.18.6_3tcywg5dy5qhcmsv6sy2pt6lua
|
||||||
next-themes: 0.2.1_jcrpix7mbfpfu5movksylxa5c4
|
next-themes: 0.2.1_3tcywg5dy5qhcmsv6sy2pt6lua
|
||||||
prisma: 4.7.1
|
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y
|
react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
|
@ -78,6 +78,7 @@ dependencies:
|
||||||
react-feather: 2.0.10_react@18.2.0
|
react-feather: 2.0.10_react@18.2.0
|
||||||
react-hot-toast: 2.4.0-beta.0_owo25xnefcwdq3zjgtohz6dbju
|
react-hot-toast: 2.4.0-beta.0_owo25xnefcwdq3zjgtohz6dbju
|
||||||
server-only: 0.0.1
|
server-only: 0.0.1
|
||||||
|
swr: 2.0.0_react@18.2.0
|
||||||
textarea-markdown-editor: 1.0.4_biqbaboplfbrettd7655fr4n2y
|
textarea-markdown-editor: 1.0.4_biqbaboplfbrettd7655fr4n2y
|
||||||
ts-jest: 29.0.3_7hcmezpa7bajbjecov7p46z4aa
|
ts-jest: 29.0.3_7hcmezpa7bajbjecov7p46z4aa
|
||||||
uuid: 9.0.0
|
uuid: 9.0.0
|
||||||
|
@ -103,6 +104,7 @@ devDependencies:
|
||||||
eslint-config-next: 13.0.3_hsmo2rtalirsvadpuxki35bq2i
|
eslint-config-next: 13.0.3_hsmo2rtalirsvadpuxki35bq2i
|
||||||
next-unused: 0.0.6
|
next-unused: 0.0.6
|
||||||
prettier: 2.6.2
|
prettier: 2.6.2
|
||||||
|
prisma: 4.8.0
|
||||||
typescript: 4.6.4
|
typescript: 4.6.4
|
||||||
typescript-plugin-css-modules: 3.4.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
|
'@jridgewell/sourcemap-codec': 1.4.14
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@next-auth/prisma-adapter/1.0.5_64qbzg5ec56bux2misz3l4u6g4:
|
/@next-auth/prisma-adapter/1.0.5_fmf72d7n4jt7coiyftaa4dlrhe:
|
||||||
resolution: {integrity: sha512-VqMS11IxPXrPGXw6Oul6jcyS/n8GLOWzRMrPr3EMdtD6eOalM6zz05j08PcNiis8QzkfuYnCv49OvufTuaEwYQ==}
|
resolution: {integrity: sha512-VqMS11IxPXrPGXw6Oul6jcyS/n8GLOWzRMrPr3EMdtD6eOalM6zz05j08PcNiis8QzkfuYnCv49OvufTuaEwYQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@prisma/client': '>=2.26.0 || >=3'
|
'@prisma/client': '>=2.26.0 || >=3'
|
||||||
next-auth: ^4
|
next-auth: ^4
|
||||||
dependencies:
|
dependencies:
|
||||||
'@prisma/client': 4.7.1_prisma@4.7.1
|
'@prisma/client': 4.8.0_prisma@4.8.0
|
||||||
next-auth: 4.18.6_jcrpix7mbfpfu5movksylxa5c4
|
next-auth: 4.18.6_3tcywg5dy5qhcmsv6sy2pt6lua
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@next/bundle-analyzer/13.0.7-canary.4:
|
/@next/bundle-analyzer/13.0.7-canary.4:
|
||||||
|
@ -804,8 +806,8 @@ packages:
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@next/env/13.1.1-canary.1:
|
/@next/env/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-h8DEj69dLJpFUuXOVPQeJ4/X1LbW5mtZSsaS5Xr/pt2VbrRN50eAV/6rMY+l6U6p/4AX1/F5aK4UBzLQJbwFzw==}
|
resolution: {integrity: sha512-8IwZJ557A7L81FsDGH64/u1oHgBGNW1zlpVGukFXUPh5XF3j/OMtaPR+gxGa59OTDuWMiydKrikX+1Obv7xwcg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@next/eslint-plugin-next/13.0.3:
|
/@next/eslint-plugin-next/13.0.3:
|
||||||
|
@ -814,18 +816,18 @@ packages:
|
||||||
glob: 7.1.7
|
glob: 7.1.7
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@next/eslint-plugin-next/13.1.1-canary.1:
|
/@next/eslint-plugin-next/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-gKabWQJ+Aps/u/lzVC3FoGwbG+r1t3cwoUXPbcpC3igrpBSbkhEoK9u3MYeMlkAUywJ7IvsENWVYqjzRuaso4Q==}
|
resolution: {integrity: sha512-EeH9njTYTmaM814ByCF9B+KPR3tEYYtrGoqrNLGd6asMNRG6q49dgsE+JxdZ+vYws6NItHqL645W6bqANK0Vhg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
glob: 7.1.7
|
glob: 7.1.7
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@next/font/13.1.1-canary.1:
|
/@next/font/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-cygeAS0h5OuWaorcQ6Ry8c/E0fwZEGQfZy7kUjXHVn6DP4sekB2RgR9aNWL3cUoJ35RwjwtsR7K6FufUyzfGag==}
|
resolution: {integrity: sha512-HfutDb/73yxmqO7UOsh1It2jLtKCJq6meEZxIwSulikWCqRukxs25Li3tUq+r6eDXkU2y6cfVv1MxNc6I3lTBA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@next/swc-android-arm-eabi/13.1.1-canary.1:
|
/@next/swc-android-arm-eabi/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-0McGEjTnNXdBTlghWxkuM07qpKMr44afLeGFpS/zwIlDV7lNOXFzCpyHdJoJsFL4kBJgfbyCi8aamnhwqlwZxA==}
|
resolution: {integrity: sha512-h30pxaiAUiZqkYDlcLTIVkXswPB0U3zubjel4ejJ2/SszbQrd9JTfj20Kq25IPjKIYOll8kRr/pIX4iukuGGmw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [android]
|
os: [android]
|
||||||
|
@ -833,8 +835,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-android-arm64/13.1.1-canary.1:
|
/@next/swc-android-arm64/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-XCmPqmhtsc52lv0Qs/maThRrQpHMRK1AqFhgFXfFG9wclbFBtQIUapD/qD7nOlXbch+7RDONbABPf/8pE2T0cQ==}
|
resolution: {integrity: sha512-QNdrO5rFxqHaNBUHYKfo8iGI9VYkyNhxTF/66JVv23IxKneYzS2QyuFH3S2xCxTGtxsQVPmRf1W9UA4dfjU70A==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [android]
|
os: [android]
|
||||||
|
@ -842,8 +844,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-darwin-arm64/13.1.1-canary.1:
|
/@next/swc-darwin-arm64/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-qz+et20cTetOppH6stlDW171tTo1vG4eHGmXY1Zwa3D/sZPk5IRsqsmpdzxuBsVxdk5x7zaliYZowOlQM2awnw==}
|
resolution: {integrity: sha512-cJfA2blHqFAWvduKSGNxSE5ttowjIY4yYaGx+cbvq5j48D8Fe8QhWgTLwMVeIE7PA+kUoIsZrxLRoJyNFry6bQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
@ -851,8 +853,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-darwin-x64/13.1.1-canary.1:
|
/@next/swc-darwin-x64/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-rPGOUsxturFtSkqtbSis1kBuu0mNzCPibWEMihsM32EzdXeDXJMgl5EP3+RiwGfrawON5lcTEz0r52Zll+0kmw==}
|
resolution: {integrity: sha512-9loctTiLf7GyuTAuujH/OOabdAElHoVthpt9ELtgVByE2aUpQJpBdgeVc5SWDozlrxKnuDV+TvFOJaodLiF5BA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
@ -860,8 +862,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-freebsd-x64/13.1.1-canary.1:
|
/@next/swc-freebsd-x64/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-tEnpdXSEzltEEbeh32w4eQN1znR35xjX0pMC7leud8XhJvppWwdEqfdOp3OuviPmb8p6LzFqYyknNe710cFy+Q==}
|
resolution: {integrity: sha512-ptz2x8HnT8VeFNlZXSF5J2IbaLDZFzGfWcbSvU3PiOCZPMje4ROdnUxTM4yLSFTrw1uA47a8rWlBQxfrO1c/eA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [freebsd]
|
os: [freebsd]
|
||||||
|
@ -869,8 +871,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-arm-gnueabihf/13.1.1-canary.1:
|
/@next/swc-linux-arm-gnueabihf/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-EJdCFjRHVoyDC8Q0N8ULCJ7+5hl4NhaELlu+04cCcgQ3qFZkFZIfTLrXnCT1aa2Y8yyR5FvyBeHgvusL5abqpQ==}
|
resolution: {integrity: sha512-lzJRomdDC4qZ8W1kmZ24SvyQG2Q8RuAe4F00x4TEuecy4kt+PfDmRRmHpNMr9JIl4/KfYhFSK730dfsVos/f9g==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
@ -878,8 +880,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-arm64-gnu/13.1.1-canary.1:
|
/@next/swc-linux-arm64-gnu/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-BRN7Beg1OASa2F7FGYAdYL3O+bA2wFX6ow9QnHD312+JHCf/IKun3FSxSXBaSnc8ZJCnexmSWIz+hewKN1jGQQ==}
|
resolution: {integrity: sha512-EtigR57JBv2l5oHf8FJGUrVfe+Lx60gKrcosnoMoIf9YO9HhSfmbiaueO9heOegSBQVBsbjBhx807WTVqRTT+Q==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
@ -887,8 +889,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-arm64-musl/13.1.1-canary.1:
|
/@next/swc-linux-arm64-musl/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-WE1muJmocpSHUHBH02iMOy9RR4Hz7XFM6tjAevY40svXNmGNszhYzsm0MQ+/VnlqP9f9l1/dEiPN6tSbMAlH9A==}
|
resolution: {integrity: sha512-9snjPWQ2JdF8hRzwyOLzHLDPGPkysGRyl34uvG0YQ4/I6Hsk/zVdG7gSVtODMzyNKs6oQTA036SjDq9vGyzpPQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
@ -896,8 +898,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-x64-gnu/13.1.1-canary.1:
|
/@next/swc-linux-x64-gnu/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-aeBiutM8gFndpUkDA6t8DKzD9TcYg48+b7QxuL2XyRJo+47muhNbXaB6y/MwarxwjnsAry0hMs/ycP3lOL7vnw==}
|
resolution: {integrity: sha512-jQyRP7oQwK4EDH8pPZSNS1K94azTDHvGF2fA6QQwxntSB6ek3CS9EGHMzNquV3gsDLtBFP2LdWiQJ3+uWT4qyw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
@ -905,8 +907,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-x64-musl/13.1.1-canary.1:
|
/@next/swc-linux-x64-musl/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-JyJzejDuu68bZj1jrdbgJEIyj0xQy8N0R363T6Rx5/F5Htk2vVzXaP+MkANcWuZjvmH/BHjQc515liiTwQ328Q==}
|
resolution: {integrity: sha512-VQHhBjbGXZEAWLP9DG1O0ZG4GltG04oG3TzDjY+NllcAnSiwQ/KwZgwmbAqQqvbYqTEbMmz45YNUqB9MiyMZZg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
@ -914,8 +916,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-arm64-msvc/13.1.1-canary.1:
|
/@next/swc-win32-arm64-msvc/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-y/VxMhjXrTt4fGzrJwdfa6MM2ZauZ0dX20aRGDX/6VeaxO5toBsmXF7cwoDC97C65l93FY/X9vyc75WSLrXFrA==}
|
resolution: {integrity: sha512-YOkIOf5iJB32ouK+XJB+yuAKwzXH2r5pXpvJAHH8105tI7B6Bj8GkaUjoMsBb6qSFudE2Axn38r8XRmec4y7Uw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
@ -923,8 +925,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-ia32-msvc/13.1.1-canary.1:
|
/@next/swc-win32-ia32-msvc/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-Nk1DdvC+Ocdqnj4Ra+qWJK/PQ68hrWmSg3FXL4I3pooX2IZcUSF8nPFNS0r8V47inTAXbwatcFEKSBRjFBS2ww==}
|
resolution: {integrity: sha512-wOU1b+8Qma47i5CNlZBparatFxIyEdNTXmjv/ce/C/O34i02uYTBo9iNId8K6OCJ/T9W1o6fRJItHoDKBUg3cg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [ia32]
|
cpu: [ia32]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
@ -932,8 +934,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-x64-msvc/13.1.1-canary.1:
|
/@next/swc-win32-x64-msvc/13.1.2-canary.0:
|
||||||
resolution: {integrity: sha512-/7q6tjUebSaUYTGZRpp4qAmrcL6+tiKfHN5YgW6zpX5MWLEk1DkdnuBjO/jSvCJd0510byBkN6drlzmfTMjzzg==}
|
resolution: {integrity: sha512-E/YYo47qCRFIat2G8doU5M09We10xq9K3hPukZHqouwk7gQBj7HUhT5p08FtXytqOljzBzNv5E7ba3aicub1Rw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
@ -973,8 +975,8 @@ packages:
|
||||||
/@popperjs/core/2.11.6:
|
/@popperjs/core/2.11.6:
|
||||||
resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
|
resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
|
||||||
|
|
||||||
/@prisma/client/4.7.1_prisma@4.7.1:
|
/@prisma/client/4.8.0_prisma@4.8.0:
|
||||||
resolution: {integrity: sha512-/GbnOwIPtjiveZNUzGXOdp7RxTEkHL4DZP3vBaFNadfr6Sf0RshU5EULFzVaSi9i9PIK9PYd+1Rn7z2B2npb9w==}
|
resolution: {integrity: sha512-Y1riB0p2W52kh3zgssP/YAhln3RjBFcJy3uwEiyjmU+TQYh6QTZDRFBo3JtBWuq2FyMOl1Rye8jxzUP+n0l5Cg==}
|
||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -983,18 +985,17 @@ packages:
|
||||||
prisma:
|
prisma:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@prisma/engines-version': 4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c
|
'@prisma/engines-version': 4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe
|
||||||
prisma: 4.7.1
|
prisma: 4.8.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@prisma/engines-version/4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c:
|
/@prisma/engines-version/4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe:
|
||||||
resolution: {integrity: sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==}
|
resolution: {integrity: sha512-MHSOSexomRMom8QN4t7bu87wPPD+pa+hW9+71JnVcF3DqyyO/ycCLhRL1we3EojRpZxKvuyGho2REQsMCvxcJw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@prisma/engines/4.7.1:
|
/@prisma/engines/4.8.0:
|
||||||
resolution: {integrity: sha512-zWabHosTdLpXXlMefHmnouhXMoTB1+SCbUU3t4FCmdrtIOZcarPKU3Alto7gm/pZ9vHlGOXHCfVZ1G7OIrSbog==}
|
resolution: {integrity: sha512-A1Asn2rxZMlLAj1HTyfaCv0VQrLUv034jVay05QlqZg1qiHPeA3/pGTfNMijbsMYCsGVxfWEJuaZZuNxXGMCrA==}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@radix-ui/primitive/1.0.0:
|
/@radix-ui/primitive/1.0.0:
|
||||||
resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==}
|
resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==}
|
||||||
|
@ -1804,14 +1805,14 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@wits/next-themes/0.2.14_jcrpix7mbfpfu5movksylxa5c4:
|
/@wits/next-themes/0.2.14_3tcywg5dy5qhcmsv6sy2pt6lua:
|
||||||
resolution: {integrity: sha512-fHKb/tRcWbYNblGHZtfvAQztDhzUB9d7ZkYOny0BisSPh6EABcsqxKB48ABUQztcmKywlp2zEMkLcSRj/PQBSw==}
|
resolution: {integrity: sha512-fHKb/tRcWbYNblGHZtfvAQztDhzUB9d7ZkYOny0BisSPh6EABcsqxKB48ABUQztcmKywlp2zEMkLcSRj/PQBSw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
next: '*'
|
next: '*'
|
||||||
react: '*'
|
react: '*'
|
||||||
react-dom: '*'
|
react-dom: '*'
|
||||||
dependencies:
|
dependencies:
|
||||||
next: 13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y
|
next: 13.1.2-canary.0_biqbaboplfbrettd7655fr4n2y
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -5217,7 +5218,7 @@ packages:
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/next-auth/4.18.6_jcrpix7mbfpfu5movksylxa5c4:
|
/next-auth/4.18.6_3tcywg5dy5qhcmsv6sy2pt6lua:
|
||||||
resolution: {integrity: sha512-0TQwbq5X9Jyd1wUVYUoyvHJh4JWXeW9UOcMEl245Er/Y5vsSbyGJHt8M7xjRMzk9mORVMYehoMdERgyiq/jCgA==}
|
resolution: {integrity: sha512-0TQwbq5X9Jyd1wUVYUoyvHJh4JWXeW9UOcMEl245Er/Y5vsSbyGJHt8M7xjRMzk9mORVMYehoMdERgyiq/jCgA==}
|
||||||
engines: {node: ^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0}
|
engines: {node: ^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -5233,7 +5234,7 @@ packages:
|
||||||
'@panva/hkdf': 1.0.2
|
'@panva/hkdf': 1.0.2
|
||||||
cookie: 0.5.0
|
cookie: 0.5.0
|
||||||
jose: 4.11.0
|
jose: 4.11.0
|
||||||
next: 13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y
|
next: 13.1.2-canary.0_biqbaboplfbrettd7655fr4n2y
|
||||||
oauth: 0.9.15
|
oauth: 0.9.15
|
||||||
openid-client: 5.3.0
|
openid-client: 5.3.0
|
||||||
preact: 10.11.2
|
preact: 10.11.2
|
||||||
|
@ -5243,14 +5244,14 @@ packages:
|
||||||
uuid: 8.3.2
|
uuid: 8.3.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/next-themes/0.2.1_jcrpix7mbfpfu5movksylxa5c4:
|
/next-themes/0.2.1_3tcywg5dy5qhcmsv6sy2pt6lua:
|
||||||
resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
|
resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
next: '*'
|
next: '*'
|
||||||
react: '*'
|
react: '*'
|
||||||
react-dom: '*'
|
react-dom: '*'
|
||||||
dependencies:
|
dependencies:
|
||||||
next: 13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y
|
next: 13.1.2-canary.0_biqbaboplfbrettd7655fr4n2y
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -5266,8 +5267,8 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/next/13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y:
|
/next/13.1.2-canary.0_biqbaboplfbrettd7655fr4n2y:
|
||||||
resolution: {integrity: sha512-20EeQyfGs9dGUAPrXAod5jay1plcM0itItL/7z9BMczYM55/it8TxS1OPTmseyM9Y8uuybTRoCHeKh6TCI09tg==}
|
resolution: {integrity: sha512-EUzXiMtS6tTyqyZnD3WtDPPO8K8lMNfuPd7NaxpDCEBJZ4YbeffiMpco9m5aIJpgt6uQ2TdGOuYl9zKj6WF2gw==}
|
||||||
engines: {node: '>=14.6.0'}
|
engines: {node: '>=14.6.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -5284,7 +5285,7 @@ packages:
|
||||||
sass:
|
sass:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 13.1.1-canary.1
|
'@next/env': 13.1.2-canary.0
|
||||||
'@swc/helpers': 0.4.14
|
'@swc/helpers': 0.4.14
|
||||||
caniuse-lite: 1.0.30001431
|
caniuse-lite: 1.0.30001431
|
||||||
postcss: 8.4.14
|
postcss: 8.4.14
|
||||||
|
@ -5292,19 +5293,19 @@ packages:
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
styled-jsx: 5.1.1_react@18.2.0
|
styled-jsx: 5.1.1_react@18.2.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@next/swc-android-arm-eabi': 13.1.1-canary.1
|
'@next/swc-android-arm-eabi': 13.1.2-canary.0
|
||||||
'@next/swc-android-arm64': 13.1.1-canary.1
|
'@next/swc-android-arm64': 13.1.2-canary.0
|
||||||
'@next/swc-darwin-arm64': 13.1.1-canary.1
|
'@next/swc-darwin-arm64': 13.1.2-canary.0
|
||||||
'@next/swc-darwin-x64': 13.1.1-canary.1
|
'@next/swc-darwin-x64': 13.1.2-canary.0
|
||||||
'@next/swc-freebsd-x64': 13.1.1-canary.1
|
'@next/swc-freebsd-x64': 13.1.2-canary.0
|
||||||
'@next/swc-linux-arm-gnueabihf': 13.1.1-canary.1
|
'@next/swc-linux-arm-gnueabihf': 13.1.2-canary.0
|
||||||
'@next/swc-linux-arm64-gnu': 13.1.1-canary.1
|
'@next/swc-linux-arm64-gnu': 13.1.2-canary.0
|
||||||
'@next/swc-linux-arm64-musl': 13.1.1-canary.1
|
'@next/swc-linux-arm64-musl': 13.1.2-canary.0
|
||||||
'@next/swc-linux-x64-gnu': 13.1.1-canary.1
|
'@next/swc-linux-x64-gnu': 13.1.2-canary.0
|
||||||
'@next/swc-linux-x64-musl': 13.1.1-canary.1
|
'@next/swc-linux-x64-musl': 13.1.2-canary.0
|
||||||
'@next/swc-win32-arm64-msvc': 13.1.1-canary.1
|
'@next/swc-win32-arm64-msvc': 13.1.2-canary.0
|
||||||
'@next/swc-win32-ia32-msvc': 13.1.1-canary.1
|
'@next/swc-win32-ia32-msvc': 13.1.2-canary.0
|
||||||
'@next/swc-win32-x64-msvc': 13.1.1-canary.1
|
'@next/swc-win32-x64-msvc': 13.1.2-canary.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
|
@ -5818,14 +5819,13 @@ packages:
|
||||||
parse-ms: 2.1.0
|
parse-ms: 2.1.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/prisma/4.7.1:
|
/prisma/4.8.0:
|
||||||
resolution: {integrity: sha512-CCQP+m+1qZOGIZlvnL6T3ZwaU0LAleIHYFPN9tFSzjs/KL6vH9rlYbGOkTuG9Q1s6Ki5D0LJlYlW18Z9EBUpGg==}
|
resolution: {integrity: sha512-DWIhxvxt8f4h6MDd35mz7BJff+fu7HItW3WPDIEpCR3RzcOWyiHBbLQW5/DOgmf+pRLTjwXQob7kuTZVYUAw5w==}
|
||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@prisma/engines': 4.7.1
|
'@prisma/engines': 4.8.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/process-nextick-args/2.0.1:
|
/process-nextick-args/2.0.1:
|
||||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||||
|
@ -6670,6 +6670,16 @@ packages:
|
||||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||||
engines: {node: '>= 0.4'}
|
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:
|
/tapable/1.1.3:
|
||||||
resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==}
|
resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -7066,6 +7076,14 @@ packages:
|
||||||
tslib: 2.4.1
|
tslib: 2.4.1
|
||||||
dev: false
|
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:
|
/util-deprecate/1.0.2:
|
||||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,7 @@ model User {
|
||||||
posts Post[]
|
posts Post[]
|
||||||
accounts Account[]
|
accounts Account[]
|
||||||
sessions Session[]
|
sessions Session[]
|
||||||
|
apiTokens ApiToken[]
|
||||||
// below are added for CredentialProvider
|
// below are added for CredentialProvider
|
||||||
username String? @unique
|
username String? @unique
|
||||||
password String? @map("hashed_password")
|
password String? @map("hashed_password")
|
||||||
|
@ -111,3 +112,16 @@ model VerificationToken {
|
||||||
@@unique([identifier, token])
|
@@unique([identifier, token])
|
||||||
@@map("verification_tokens")
|
@@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"
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
import { JWT } from "next-auth/jwt"
|
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
|
type UserId = string
|
||||||
|
|
||||||
|
@ -7,6 +9,7 @@ declare module "next-auth/jwt" {
|
||||||
interface JWT {
|
interface JWT {
|
||||||
id: UserId
|
id: UserId
|
||||||
role: string
|
role: string
|
||||||
|
sessionToken: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +18,7 @@ declare module "next-auth" {
|
||||||
user: User & {
|
user: User & {
|
||||||
id: UserId
|
id: UserId
|
||||||
role: string
|
role: string
|
||||||
|
sessionToken: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,5 +28,6 @@ declare module "next-auth" {
|
||||||
email?: string | null
|
email?: string | null
|
||||||
role?: string | null
|
role?: string | null
|
||||||
id: UserId
|
id: UserId
|
||||||
|
token?: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue