rework cookies
This commit is contained in:
parent
9b9c3c1d87
commit
95d1ef31ef
17 changed files with 189 additions and 58 deletions
|
@ -9,6 +9,7 @@ import useSignedIn from "@lib/hooks/use-signed-in"
|
||||||
import Input from "@components/input"
|
import Input from "@components/input"
|
||||||
import Button from "@components/button"
|
import Button from "@components/button"
|
||||||
import Note from "@components/note"
|
import Note from "@components/note"
|
||||||
|
import { USER_COOKIE_NAME } from "@lib/constants"
|
||||||
|
|
||||||
const NO_EMPTY_SPACE_REGEX = /^\S*$/
|
const NO_EMPTY_SPACE_REGEX = /^\S*$/
|
||||||
const ERROR_MESSAGE =
|
const ERROR_MESSAGE =
|
||||||
|
@ -32,7 +33,7 @@ const Auth = ({
|
||||||
|
|
||||||
const handleJson = (json: any) => {
|
const handleJson = (json: any) => {
|
||||||
signin(json.token)
|
signin(json.token)
|
||||||
Cookies.set("drift-userid", json.userId)
|
Cookies.set(USER_COOKIE_NAME, json.userId)
|
||||||
|
|
||||||
router.push("/new")
|
router.push("/new")
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import DatePicker from "react-datepicker"
|
||||||
import getTitleForPostCopy from "@lib/get-title-for-post-copy"
|
import getTitleForPostCopy from "@lib/get-title-for-post-copy"
|
||||||
import Description from "./description"
|
import Description from "./description"
|
||||||
import { PostWithFiles } from "app/prisma"
|
import { PostWithFiles } from "app/prisma"
|
||||||
|
import { USER_COOKIE_NAME } from "@lib/constants"
|
||||||
|
|
||||||
const emptyDoc = {
|
const emptyDoc = {
|
||||||
title: "",
|
title: "",
|
||||||
|
@ -144,7 +145,7 @@ const Post = ({
|
||||||
files: docs,
|
files: docs,
|
||||||
visibility,
|
visibility,
|
||||||
password,
|
password,
|
||||||
userId: Cookies.get("drift-userid") || "",
|
userId: Cookies.get(USER_COOKIE_NAME) || "",
|
||||||
expiresAt: expiresAt || null,
|
expiresAt: expiresAt || null,
|
||||||
parentId: newPostParent
|
parentId: newPostParent
|
||||||
})
|
})
|
||||||
|
|
|
@ -18,6 +18,7 @@ import CreatedAgoBadge from "@components/badges/created-ago-badge"
|
||||||
import Cookies from "js-cookie"
|
import Cookies from "js-cookie"
|
||||||
import PasswordModalPage from "./password-modal-wrapper"
|
import PasswordModalPage from "./password-modal-wrapper"
|
||||||
import VisibilityControl from "@components/badges/visibility-control"
|
import VisibilityControl from "@components/badges/visibility-control"
|
||||||
|
import { USER_COOKIE_NAME } from "@lib/constants"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
post: Post
|
post: Post
|
||||||
|
@ -32,7 +33,7 @@ const PostPage = ({ post: initialPost, isProtected }: Props) => {
|
||||||
)
|
)
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [isOwner] = useState(
|
const [isOwner] = useState(
|
||||||
post.users ? post.users[0].id === Cookies.get("drift-userid") : false
|
post.users ? post.users[0].id === Cookies.get(USER_COOKIE_NAME) : false
|
||||||
)
|
)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const isMobile = useMediaQuery("mobile")
|
const isMobile = useMediaQuery("mobile")
|
||||||
|
|
|
@ -3,12 +3,12 @@ import { User } from "@prisma/client"
|
||||||
import prisma from "app/prisma"
|
import prisma from "app/prisma"
|
||||||
import { sign } from "jsonwebtoken"
|
import { sign } from "jsonwebtoken"
|
||||||
|
|
||||||
export async function generateAccessToken(user: User) {
|
export async function generateAndExpireAccessToken(userId: User["id"]) {
|
||||||
const token = sign({ id: user.id }, config.jwt_secret, { expiresIn: "2d" })
|
const token = sign({ id: userId }, config.jwt_secret, { expiresIn: "2d" })
|
||||||
|
|
||||||
await prisma.authTokens.create({
|
await prisma.authTokens.create({
|
||||||
data: {
|
data: {
|
||||||
userId: user.id,
|
userId: userId,
|
||||||
token: token
|
token: token
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -16,7 +16,7 @@ export async function generateAccessToken(user: User) {
|
||||||
// TODO: set expiredReason?
|
// TODO: set expiredReason?
|
||||||
prisma.authTokens.deleteMany({
|
prisma.authTokens.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
userId: user.id,
|
userId: userId,
|
||||||
token: {
|
token: {
|
||||||
not: token
|
not: token
|
||||||
}
|
}
|
||||||
|
|
25
client/lib/api/signin.ts
Normal file
25
client/lib/api/signin.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { USER_COOKIE_NAME, TOKEN_COOKIE_NAME } from "@lib/constants"
|
||||||
|
import { User } from "app/prisma"
|
||||||
|
import { setCookie } from "cookies-next"
|
||||||
|
import { NextApiRequest, NextApiResponse } from "next"
|
||||||
|
import { generateAndExpireAccessToken } from "./generate-access-token"
|
||||||
|
|
||||||
|
export const signin = async (
|
||||||
|
userId: User["id"],
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) => {
|
||||||
|
const token = await generateAndExpireAccessToken(userId)
|
||||||
|
setCookie(USER_COOKIE_NAME, userId, {
|
||||||
|
maxAge: 30 * 24 * 60 * 60, // 30 days,
|
||||||
|
req,
|
||||||
|
res
|
||||||
|
})
|
||||||
|
setCookie(TOKEN_COOKIE_NAME, token, {
|
||||||
|
maxAge: 30 * 24 * 60 * 60, // 30 days
|
||||||
|
req,
|
||||||
|
res
|
||||||
|
})
|
||||||
|
|
||||||
|
return token
|
||||||
|
}
|
|
@ -121,3 +121,6 @@ export const allowedFileExtensions = [
|
||||||
"log",
|
"log",
|
||||||
...codeFileExtensions
|
...codeFileExtensions
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const TOKEN_COOKIE_NAME = "drift-token"
|
||||||
|
export const USER_COOKIE_NAME = "drift-userid"
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import Cookies from "js-cookie"
|
import { getCookie, setCookie } from "cookies-next"
|
||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
import useSharedState from "./use-shared-state"
|
import useSharedState from "./use-shared-state"
|
||||||
|
|
||||||
const useSignedIn = () => {
|
const useSignedIn = () => {
|
||||||
|
const token = getCookie("drift-token")
|
||||||
|
|
||||||
const [signedIn, setSignedIn] = useSharedState(
|
const [signedIn, setSignedIn] = useSharedState(
|
||||||
"signedIn",
|
"signedIn",
|
||||||
typeof window === "undefined" ? false : !!Cookies.get("drift-token")
|
typeof window === "undefined" ? false : !!token
|
||||||
)
|
)
|
||||||
const token = Cookies.get("drift-token")
|
|
||||||
const signin = (token: string) => {
|
const signin = (token: string) => {
|
||||||
setSignedIn(true)
|
setSignedIn(true)
|
||||||
// TODO: investigate SameSite / CORS cookie security
|
// TODO: investigate SameSite / CORS cookie security
|
||||||
Cookies.set("drift-token", token)
|
setCookie("drift-token", token)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
60
client/lib/providers/auth/AuthClientContextProvider.tsx
Normal file
60
client/lib/providers/auth/AuthClientContextProvider.tsx
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import clsx from "clsx";
|
||||||
|
import type {
|
||||||
|
ChangeEventHandler,
|
||||||
|
FunctionComponent,
|
||||||
|
PropsWithChildren,
|
||||||
|
} from "react";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import React, { useContext, useState, createContext } from "react";
|
||||||
|
import { DEFAULT_THEME, Theme, THEME_COOKIE_NAME } from "./theme";
|
||||||
|
|
||||||
|
const ThemeContext = createContext<Theme | null>(null);
|
||||||
|
|
||||||
|
export function useTheme(): Theme {
|
||||||
|
return useContext(ThemeContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props extends PropsWithChildren {
|
||||||
|
defaultTheme: Theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThemeClientContextProvider: FunctionComponent<Props> = ({
|
||||||
|
defaultTheme,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const [theme, setTheme] = useState<Theme>(defaultTheme);
|
||||||
|
const onChange: ChangeEventHandler<HTMLSelectElement> = (e) => {
|
||||||
|
const value = e.target.value as Theme;
|
||||||
|
setTheme(value);
|
||||||
|
|
||||||
|
if (value === DEFAULT_THEME) {
|
||||||
|
Cookies.remove(THEME_COOKIE_NAME);
|
||||||
|
} else {
|
||||||
|
Cookies.set(THEME_COOKIE_NAME, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const onReset = () => {
|
||||||
|
setTheme(DEFAULT_THEME);
|
||||||
|
Cookies.remove(THEME_COOKIE_NAME);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={clsx(theme === "dark" && "dark")}>
|
||||||
|
<div className="mb-2">
|
||||||
|
<h2 className="mb-2 font-bold text-xl">Theme Switcher</h2>
|
||||||
|
<select value={theme} onChange={onChange} className="mr-2 inline-block">
|
||||||
|
<option value="light">Light</option>
|
||||||
|
<option value="dark">Dark</option>
|
||||||
|
</select>
|
||||||
|
<button className="bg-gray-300 p-2" onClick={onReset}>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThemeClientContextProvider;
|
27
client/lib/providers/auth/AuthProvider.tsx
Normal file
27
client/lib/providers/auth/AuthProvider.tsx
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { FunctionComponent, PropsWithChildren } from "react";
|
||||||
|
import ThemeClientContextProvider from "./ThemeClientContextProvider";
|
||||||
|
import ThemeServerContextProvider, {
|
||||||
|
useServerTheme,
|
||||||
|
} from "./ThemeServerContextProvider";
|
||||||
|
|
||||||
|
const ThemeProviderWrapper: FunctionComponent<PropsWithChildren> = ({
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const theme = useServerTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeClientContextProvider defaultTheme={theme}>
|
||||||
|
{children}
|
||||||
|
</ThemeClientContextProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ThemeProvider: FunctionComponent<PropsWithChildren> = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<ThemeServerContextProvider>
|
||||||
|
<ThemeProviderWrapper>{children}</ThemeProviderWrapper>
|
||||||
|
</ThemeServerContextProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThemeProvider;
|
25
client/lib/providers/auth/AuthServerContextProvider.tsx
Normal file
25
client/lib/providers/auth/AuthServerContextProvider.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import type { FunctionComponent, PropsWithChildren } from "react";
|
||||||
|
// @ts-ignore -- createServerContext is not in @types/react atm
|
||||||
|
import { useContext, createServerContext } from "react";
|
||||||
|
import { cookies } from "next/headers";
|
||||||
|
import { Theme, THEME_COOKIE_NAME } from "./theme";
|
||||||
|
import { DEFAULT_THEME } from "./theme";
|
||||||
|
|
||||||
|
const ThemeContext = createServerContext<Theme | null>(null);
|
||||||
|
|
||||||
|
export function useServerTheme(): Theme {
|
||||||
|
return useContext(ThemeContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThemeServerContextProvider: FunctionComponent<PropsWithChildren> = ({
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const cookiesList = cookies();
|
||||||
|
const theme = cookiesList.get(THEME_COOKIE_NAME) ?? DEFAULT_THEME;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThemeServerContextProvider;
|
|
@ -1,11 +1,12 @@
|
||||||
import { NextFetchEvent, NextResponse } from "next/server"
|
import { NextFetchEvent, NextResponse } from "next/server"
|
||||||
import type { NextRequest } from "next/server"
|
import type { NextRequest } from "next/server"
|
||||||
|
import { TOKEN_COOKIE_NAME, USER_COOKIE_NAME } from "@lib/constants"
|
||||||
|
|
||||||
const PUBLIC_FILE = /\.(.*)$/
|
const PUBLIC_FILE = /\.(.*)$/
|
||||||
|
|
||||||
export function middleware(req: NextRequest, event: NextFetchEvent) {
|
export function middleware(req: NextRequest, event: NextFetchEvent) {
|
||||||
const pathname = req.nextUrl.pathname
|
const pathname = req.nextUrl.pathname
|
||||||
const signedIn = req.cookies.get("drift-token")
|
const signedIn = req.cookies.get(TOKEN_COOKIE_NAME)
|
||||||
const getURL = (pageName: string) => new URL(`/${pageName}`, req.url).href
|
const getURL = (pageName: string) => new URL(`/${pageName}`, req.url).href
|
||||||
const isPageRequest =
|
const isPageRequest =
|
||||||
!PUBLIC_FILE.test(pathname) &&
|
!PUBLIC_FILE.test(pathname) &&
|
||||||
|
@ -17,8 +18,8 @@ export function middleware(req: NextRequest, event: NextFetchEvent) {
|
||||||
// If you're not signed in we redirect to the home page
|
// If you're not signed in we redirect to the home page
|
||||||
if (signedIn) {
|
if (signedIn) {
|
||||||
const resp = NextResponse.redirect(getURL(""))
|
const resp = NextResponse.redirect(getURL(""))
|
||||||
resp.cookies.delete("drift-token")
|
resp.cookies.delete(TOKEN_COOKIE_NAME)
|
||||||
resp.cookies.delete("drift-userid")
|
resp.cookies.delete(USER_COOKIE_NAME)
|
||||||
const signoutPromise = new Promise((resolve) => {
|
const signoutPromise = new Promise((resolve) => {
|
||||||
fetch(`${process.env.API_URL}/auth/signout`, {
|
fetch(`${process.env.API_URL}/auth/signout`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|
|
@ -15,14 +15,11 @@
|
||||||
"@geist-ui/core": "^2.3.8",
|
"@geist-ui/core": "^2.3.8",
|
||||||
"@geist-ui/icons": "1.0.2",
|
"@geist-ui/icons": "1.0.2",
|
||||||
"@prisma/client": "^4.6.0",
|
"@prisma/client": "^4.6.0",
|
||||||
"@types/cookie": "0.5.1",
|
|
||||||
"@types/js-cookie": "3.0.2",
|
|
||||||
"bcrypt": "^5.1.0",
|
"bcrypt": "^5.1.0",
|
||||||
"client-zip": "2.2.1",
|
"client-zip": "2.2.1",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"cookie": "0.5.0",
|
"cookies-next": "^2.1.1",
|
||||||
"dotenv": "16.0.0",
|
"dotenv": "16.0.0",
|
||||||
"js-cookie": "3.0.1",
|
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"marked": "^4.2.2",
|
"marked": "^4.2.2",
|
||||||
"next": "13.0.3-canary.2",
|
"next": "13.0.3-canary.2",
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import config from "@lib/config"
|
|
||||||
import { NextApiRequest, NextApiResponse } from "next"
|
import { NextApiRequest, NextApiResponse } from "next"
|
||||||
import prisma from "app/prisma"
|
import prisma from "app/prisma"
|
||||||
import bcrypt from "bcrypt"
|
import bcrypt from "bcrypt"
|
||||||
import { generateAccessToken } from "@lib/api/generate-access-token"
|
import { signin } from "@lib/api/signin"
|
||||||
import Cookies from "js-cookie"
|
|
||||||
|
|
||||||
export default async function handler(
|
export default async function handler(
|
||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
|
@ -29,8 +27,7 @@ export default async function handler(
|
||||||
return res.status(401).json({ error: "Unauthorized" })
|
return res.status(401).json({ error: "Unauthorized" })
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = await generateAccessToken(user)
|
const token = await signin(user.id, req, res);
|
||||||
Cookies.set("drift-user", user.id, { path: "/" })
|
|
||||||
Cookies.set("drift-token", token, { path: "/" })
|
|
||||||
return res.status(201).json({ token: token, userId: user.id })
|
return res.status(201).json({ token: token, userId: user.id })
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import config from "@lib/config"
|
||||||
import { NextApiRequest, NextApiResponse } from "next"
|
import { NextApiRequest, NextApiResponse } from "next"
|
||||||
import prisma from "app/prisma"
|
import prisma from "app/prisma"
|
||||||
import bcrypt, { genSalt } from "bcrypt"
|
import bcrypt, { genSalt } from "bcrypt"
|
||||||
import { generateAccessToken } from "@lib/api/generate-access-token"
|
import { generateAndExpireAccessToken } from "@lib/api/generate-access-token"
|
||||||
|
|
||||||
export default async function handler(
|
export default async function handler(
|
||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
|
@ -35,7 +35,7 @@ export default async function handler(
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const token = await generateAccessToken(user)
|
const token = await generateAndExpireAccessToken(user)
|
||||||
|
|
||||||
return res.status(201).json({ token: token, userId: user.id })
|
return res.status(201).json({ token: token, userId: user.id })
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,14 @@
|
||||||
import { withJwt } from "@lib/api/jwt"
|
|
||||||
import config from "@lib/config"
|
import config from "@lib/config"
|
||||||
import { NextApiRequest, NextApiResponse } from "next"
|
import { NextApiRequest, NextApiResponse } from "next"
|
||||||
|
|
||||||
const handleSelf = async (
|
const handleSelf = async (req: NextApiRequest, res: NextApiResponse) => {}
|
||||||
req: NextApiRequest,
|
|
||||||
res: NextApiResponse
|
|
||||||
) => {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const PATH_TO_HANDLER = {
|
const PATH_TO_HANDLER = {
|
||||||
"self": handleRequiresPasscode
|
self: handleRequiresPasscode
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-anonymous-default-export
|
// eslint-disable-next-line import/no-anonymous-default-export
|
||||||
export default withJwt((req: NextApiRequest, res: NextApiResponse) => {
|
export default (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { slug } = req.query
|
const { slug } = req.query
|
||||||
|
|
||||||
if (!slug || Array.isArray(slug)) {
|
if (!slug || Array.isArray(slug)) {
|
||||||
|
@ -29,4 +23,4 @@ export default withJwt((req: NextApiRequest, res: NextApiResponse) => {
|
||||||
default:
|
default:
|
||||||
return res.status(405).json({ error: "Method not allowed" })
|
return res.status(405).json({ error: "Method not allowed" })
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import type { GetServerSideProps } from "next"
|
||||||
|
|
||||||
import type { Post } from "@lib/types"
|
import type { Post } from "@lib/types"
|
||||||
import PostPage from "@components/post-page"
|
import PostPage from "@components/post-page"
|
||||||
|
import { USER_COOKIE_NAME } from "@lib/constants"
|
||||||
|
|
||||||
export type PostProps = {
|
export type PostProps = {
|
||||||
post: Post
|
post: Post
|
||||||
|
@ -47,7 +48,7 @@ export const getServerSideProps: GetServerSideProps = async ({
|
||||||
|
|
||||||
const json = (await post.json()) as Post
|
const json = (await post.json()) as Post
|
||||||
const isAuthor = json.users?.find(
|
const isAuthor = json.users?.find(
|
||||||
(user) => user.id === req.cookies["drift-userid"]
|
(user) => user.id === req.cookies[USER_COOKIE_NAME]
|
||||||
)
|
)
|
||||||
|
|
||||||
if (json.visibility === "public" || json.visibility === "unlisted") {
|
if (json.visibility === "public" || json.visibility === "unlisted") {
|
||||||
|
|
|
@ -6,8 +6,6 @@ specifiers:
|
||||||
'@next/bundle-analyzer': 12.1.6
|
'@next/bundle-analyzer': 12.1.6
|
||||||
'@prisma/client': ^4.6.0
|
'@prisma/client': ^4.6.0
|
||||||
'@types/bcrypt': ^5.0.0
|
'@types/bcrypt': ^5.0.0
|
||||||
'@types/cookie': 0.5.1
|
|
||||||
'@types/js-cookie': 3.0.2
|
|
||||||
'@types/jsonwebtoken': ^8.5.9
|
'@types/jsonwebtoken': ^8.5.9
|
||||||
'@types/marked': ^4.0.7
|
'@types/marked': ^4.0.7
|
||||||
'@types/node': 17.0.23
|
'@types/node': 17.0.23
|
||||||
|
@ -17,12 +15,11 @@ specifiers:
|
||||||
bcrypt: ^5.1.0
|
bcrypt: ^5.1.0
|
||||||
client-zip: 2.2.1
|
client-zip: 2.2.1
|
||||||
clsx: ^1.2.1
|
clsx: ^1.2.1
|
||||||
cookie: 0.5.0
|
cookies-next: ^2.1.1
|
||||||
cross-env: 7.0.3
|
cross-env: 7.0.3
|
||||||
dotenv: 16.0.0
|
dotenv: 16.0.0
|
||||||
eslint: 8.27.0
|
eslint: 8.27.0
|
||||||
eslint-config-next: 13.0.2
|
eslint-config-next: 13.0.2
|
||||||
js-cookie: 3.0.1
|
|
||||||
jsonwebtoken: ^8.5.1
|
jsonwebtoken: ^8.5.1
|
||||||
marked: ^4.2.2
|
marked: ^4.2.2
|
||||||
next: 13.0.3-canary.2
|
next: 13.0.3-canary.2
|
||||||
|
@ -49,14 +46,11 @@ dependencies:
|
||||||
'@geist-ui/core': 2.3.8_biqbaboplfbrettd7655fr4n2y
|
'@geist-ui/core': 2.3.8_biqbaboplfbrettd7655fr4n2y
|
||||||
'@geist-ui/icons': 1.0.2_zhza2kbnl2wkkf7vqdl3ton2f4
|
'@geist-ui/icons': 1.0.2_zhza2kbnl2wkkf7vqdl3ton2f4
|
||||||
'@prisma/client': 4.6.0_prisma@4.6.0
|
'@prisma/client': 4.6.0_prisma@4.6.0
|
||||||
'@types/cookie': 0.5.1
|
|
||||||
'@types/js-cookie': 3.0.2
|
|
||||||
bcrypt: 5.1.0
|
bcrypt: 5.1.0
|
||||||
client-zip: 2.2.1
|
client-zip: 2.2.1
|
||||||
clsx: 1.2.1
|
clsx: 1.2.1
|
||||||
cookie: 0.5.0
|
cookies-next: 2.1.1
|
||||||
dotenv: 16.0.0
|
dotenv: 16.0.0
|
||||||
js-cookie: 3.0.1
|
|
||||||
jsonwebtoken: 8.5.1
|
jsonwebtoken: 8.5.1
|
||||||
marked: 4.2.2
|
marked: 4.2.2
|
||||||
next: 13.0.3-canary.2_biqbaboplfbrettd7655fr4n2y
|
next: 13.0.3-canary.2_biqbaboplfbrettd7655fr4n2y
|
||||||
|
@ -415,12 +409,8 @@ packages:
|
||||||
'@types/node': 17.0.23
|
'@types/node': 17.0.23
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/cookie/0.5.1:
|
/@types/cookie/0.4.1:
|
||||||
resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==}
|
resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/js-cookie/3.0.2:
|
|
||||||
resolution: {integrity: sha512-6+0ekgfusHftJNYpihfkMu8BWdeHs9EOJuGcSofErjstGPfPGEu9yTu4t460lTzzAMl2cM5zngQJqPMHbbnvYA==}
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/json5/0.0.29:
|
/@types/json5/0.0.29:
|
||||||
|
@ -437,6 +427,10 @@ packages:
|
||||||
resolution: {integrity: sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw==}
|
resolution: {integrity: sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/node/16.18.3:
|
||||||
|
resolution: {integrity: sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/node/17.0.23:
|
/@types/node/17.0.23:
|
||||||
resolution: {integrity: sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==}
|
resolution: {integrity: sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -961,11 +955,19 @@ packages:
|
||||||
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
|
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/cookie/0.5.0:
|
/cookie/0.4.2:
|
||||||
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
|
resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/cookies-next/2.1.1:
|
||||||
|
resolution: {integrity: sha512-AZGZPdL1hU3jCjN2UMJTGhLOYzNUN9Gm+v8BdptYIHUdwz397Et1p+sZRfvAl8pKnnmMdX2Pk9xDRKCGBum6GA==}
|
||||||
|
dependencies:
|
||||||
|
'@types/cookie': 0.4.1
|
||||||
|
'@types/node': 16.18.3
|
||||||
|
cookie: 0.4.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/copy-anything/2.0.6:
|
/copy-anything/2.0.6:
|
||||||
resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==}
|
resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -2256,11 +2258,6 @@ packages:
|
||||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/js-cookie/3.0.1:
|
|
||||||
resolution: {integrity: sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/js-sdsl/4.1.5:
|
/js-sdsl/4.1.5:
|
||||||
resolution: {integrity: sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==}
|
resolution: {integrity: sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
Loading…
Reference in a new issue