diff --git a/.eslintrc.json b/.eslintrc.json index dd1b004..0c1dfca 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,9 +1,30 @@ { - "extends": ["next", "next/core-web-vitals", "plugin:prettier/recommended"], - "plugins": ["prettier", "simple-import-sort"], + "extends": [ + "next", + "next/core-web-vitals", + "plugin:prettier/recommended", + "plugin:import/recommended", + "plugin:import/typescript" + ], + "plugins": ["prettier", "import"], "rules": { "prettier/prettier": "error", - "simple-import-sort/imports": "error", - "simple-import-sort/exports": "error" + "import/order": [ + "error", + { + "groups": [ + "builtin", + "external", + "internal", + "parent", + "sibling", + "index", + "object", + "type" + ], + "newlines-between": "always" + } + ], + "@next/next/no-page-custom-font": "off" } } diff --git a/package.json b/package.json index 077d189..fa1b13d 100644 --- a/package.json +++ b/package.json @@ -19,50 +19,50 @@ "prisma:seed": "prisma db seed" }, "dependencies": { - "@prisma/client": "^3.6.0", - "axios": "^0.24.0", - "bcryptjs": "^2.4.3", - "cuid": "^2.1.8", - "dotenv": "^10.0.0", - "jsonwebtoken": "^8.5.1", - "lodash": "^4.17.21", + "@prisma/client": "3.6.0", + "axios": "0.24.0", + "bcryptjs": "2.4.3", + "cuid": "2.1.8", + "dotenv": "10.0.0", + "jsonwebtoken": "8.5.1", + "lodash": "4.17.21", "next": "12.0.7", - "next-connect": "^0.11.0", - "nookies": "^2.5.2", - "polished": "^4.1.3", - "rc-slider": "^9.7.5", - "react": "^17.0.2", - "react-collapsible": "^2.8.4", - "react-color": "^2.19.3", - "react-dom": "^17.0.2", - "react-icons": "^4.3.1", - "sharp": "^0.29.3", - "styled-components": "^5.3.3" + "next-connect": "0.11.0", + "nookies": "2.5.2", + "polished": "4.1.3", + "rc-slider": "9.7.5", + "react": "17.0.2", + "react-collapsible": "2.8.4", + "react-color": "2.19.3", + "react-dom": "17.0.2", + "react-icons": "4.3.1", + "sharp": "0.29.3", + "styled-components": "5.3.3" }, "devDependencies": { "@commitlint/cli": "15.0.0", "@commitlint/config-conventional": "15.0.0", - "@types/bcryptjs": "^2.4.2", - "@types/jsonwebtoken": "^8.5.6", - "@types/lodash": "^4.14.178", - "@types/node": "^17.0.0", + "@types/bcryptjs": "2.4.2", + "@types/jsonwebtoken": "8.5.6", + "@types/lodash": "4.14.178", + "@types/node": "17.0.0", "@types/react": "17.0.37", - "@types/react-color": "^3.0.6", - "@types/styled-components": "^5.1.18", - "babel-plugin-styled-components": "^2.0.2", + "@types/react-color": "3.0.6", + "@types/styled-components": "5.1.18", + "babel-plugin-styled-components": "2.0.2", "eslint": "8.5.0", "eslint-config-next": "12.0.7", "eslint-config-prettier": "8.3.0", + "eslint-plugin-import": "2.25.3", "eslint-plugin-prettier": "4.0.0", - "eslint-plugin-simple-import-sort": "7.0.0", "husky": "7.0.4", "lint-staged": "12.1.3", "prettier": "2.5.1", "pretty-quick": "3.1.2", - "prisma": "^3.6.0", - "react-is": "^17.0.2", - "ts-node": "^10.4.0", - "typescript": "^4.5.4" + "prisma": "3.6.0", + "react-is": "17.0.2", + "ts-node": "10.4.0", + "typescript": "4.5.4" }, "prisma": { "seed": "ts-node prisma/seed.ts" diff --git a/prisma/seed.ts b/prisma/seed.ts index d40a199..e3f7c63 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -16,7 +16,7 @@ async function main() { name: 'John Doe', description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec euismod, nisl eget aliquam consectetur, nisl nunc aliquet nunc, euismod aliquam nunc nisl euismod.', - font: '"Roboto", sans-serif', + font: 'oswald', buttonBorderRadius: '4', colors: { texts: '#e11d48', diff --git a/src/components/Avatar/index.tsx b/src/components/Avatar/index.tsx index 779dd0f..4df9dc7 100644 --- a/src/components/Avatar/index.tsx +++ b/src/components/Avatar/index.tsx @@ -1,4 +1,5 @@ -import { useData } from '../../hooks/useData' +import { useData } from 'hooks/useData' + import { Wrapper } from './styles' export function Avatar() { diff --git a/src/components/Button/styles.ts b/src/components/Button/styles.ts index 4628f24..fa6a479 100644 --- a/src/components/Button/styles.ts +++ b/src/components/Button/styles.ts @@ -6,7 +6,6 @@ type ButtonProps = { backgroundColor?: string borderColor?: string borderRadius?: string - font?: string size?: 'small' | 'medium' } @@ -15,7 +14,6 @@ export const Button = styled.button.attrs((props: ButtonProps) => ({ backgroundColor: props.backgroundColor || '#e11d48', borderColor: props.borderColor || '#e11d48', borderRadius: props.borderRadius || 8, - font: props.font || 'inherit', size: props.size || 'medium' }))` display: flex; @@ -23,7 +21,6 @@ export const Button = styled.button.attrs((props: ButtonProps) => ({ justify-content: center; width: 100%; padding: 0 1rem; - font-family: ${({ font }) => font}; color: ${({ labelColor }) => labelColor}; background: ${({ backgroundColor }) => backgroundColor}; border-radius: ${({ borderRadius }) => borderRadius}px; diff --git a/src/components/Dash/ColorPicker/index.tsx b/src/components/Dash/ColorPicker/index.tsx index f7390da..a1c6e7a 100644 --- a/src/components/Dash/ColorPicker/index.tsx +++ b/src/components/Dash/ColorPicker/index.tsx @@ -1,7 +1,7 @@ import { BlockPicker } from 'react-color' -import { useData } from '../../../hooks/useData' -import { colors } from '../../../styles/colors' +import { colors } from 'constants/colors' +import { useData } from 'hooks/useData' type ColorPickerProps = { prop: diff --git a/src/components/Dash/ColorsSettings/index.tsx b/src/components/Dash/ColorsSettings/index.tsx index 5f3969e..fbb0c8b 100644 --- a/src/components/Dash/ColorsSettings/index.tsx +++ b/src/components/Dash/ColorsSettings/index.tsx @@ -1,4 +1,4 @@ -import { ColorPicker } from '../ColorPicker' +import { ColorPicker } from 'components/Dash/ColorPicker' export function ColorsSettings() { return ( diff --git a/src/components/Dash/DataSettings/index.tsx b/src/components/Dash/DataSettings/index.tsx index 86d403b..b87e00f 100644 --- a/src/components/Dash/DataSettings/index.tsx +++ b/src/components/Dash/DataSettings/index.tsx @@ -2,10 +2,10 @@ import 'rc-slider/assets/index.css' import Slider from 'rc-slider' -import { useData } from '../../../hooks/useData' -import { DescriptionInput } from '../DescriptionInput' -import { FontsSelect } from '../FontsSelect' -import { NameInput } from '../NameInput' +import { useData } from 'hooks/useData' +import { DescriptionInput } from 'components/Dash/DescriptionInput' +import { FontsSelect } from 'components/Dash/FontsSelect' +import { NameInput } from 'components/Dash/NameInput' export function DataSettings() { const { data, setData } = useData() diff --git a/src/components/Dash/DescriptionInput/index.tsx b/src/components/Dash/DescriptionInput/index.tsx index fe0a083..4eebe0f 100644 --- a/src/components/Dash/DescriptionInput/index.tsx +++ b/src/components/Dash/DescriptionInput/index.tsx @@ -1,4 +1,4 @@ -import { useData } from '../../../hooks/useData' +import { useData } from 'hooks/useData' export function DescriptionInput() { const { data, setData } = useData() diff --git a/src/components/Dash/FontsSelect/index.tsx b/src/components/Dash/FontsSelect/index.tsx index ce1e194..8bbf802 100644 --- a/src/components/Dash/FontsSelect/index.tsx +++ b/src/components/Dash/FontsSelect/index.tsx @@ -1,4 +1,6 @@ -import { useData } from '../../../hooks/useData' +import { useData } from 'hooks/useData' + +import type { Fonts } from 'types/Fonts' export function FontsSelect() { const { data, setData } = useData() @@ -11,14 +13,18 @@ export function FontsSelect() { ...data, settings: { ...data.settings, - font: e.target.value + font: e.target.value as Fonts } }) } > - - - + + + + + + + ) } diff --git a/src/components/Dash/NameInput/index.tsx b/src/components/Dash/NameInput/index.tsx index 83f3c2e..7148831 100644 --- a/src/components/Dash/NameInput/index.tsx +++ b/src/components/Dash/NameInput/index.tsx @@ -1,4 +1,4 @@ -import { useData } from '../../../hooks/useData' +import { useData } from 'hooks/useData' export function NameInput() { const { data, setData } = useData() diff --git a/src/components/Dash/SaveChangesAlert/index.tsx b/src/components/Dash/SaveChangesAlert/index.tsx index 66668b9..5b11d88 100644 --- a/src/components/Dash/SaveChangesAlert/index.tsx +++ b/src/components/Dash/SaveChangesAlert/index.tsx @@ -1,13 +1,15 @@ import { useRouter } from 'next/router' -import type { Dispatch, SetStateAction } from 'react' -import { useData } from '../../../hooks/useData' -import { api } from '../../../services/api' -import type { Data } from '../../../types/Data' -import { removeLocalStorageData } from '../../../utils/localStorage' -import { Button } from '../../Button' +import { useData } from 'hooks/useData' +import { api } from 'services/api' +import { removeLocalStorageData } from 'utils/localStorage' +import { Button } from 'components/Button' + import { ButtonsGroup, Wrapper } from './styles' +import type { Data } from '../../../types/Data' +import type { Dispatch, SetStateAction } from 'react' + type SaveChangesAlertProps = { hasUnsavedChanges: boolean setHasUnsavedChanges: Dispatch> diff --git a/src/components/Dash/index.tsx b/src/components/Dash/index.tsx index 0703b14..e900a2a 100644 --- a/src/components/Dash/index.tsx +++ b/src/components/Dash/index.tsx @@ -2,16 +2,18 @@ import { isEqual } from 'lodash' import { useEffect, useState } from 'react' import Collapsible from 'react-collapsible' -import { useAuth } from '../../hooks/useAuth' -import { useData } from '../../hooks/useData' -import type { Data } from '../../types/Data' -import { getLocalStorageData, setLocalStorageData } from '../../utils/localStorage' -import { Home } from '../Home' -import { ColorsSettings } from './ColorsSettings' -import { DataSettings } from './DataSettings' -import { SaveChangesAlert } from './SaveChangesAlert' +import { useAuth } from 'hooks/useAuth' +import { useData } from 'hooks/useData' +import { getLocalStorageData, setLocalStorageData } from 'utils/localStorage' +import { Home } from 'components/Home' +import { ColorsSettings } from 'components/Dash/ColorsSettings' +import { DataSettings } from 'components/Dash/DataSettings' +import { SaveChangesAlert } from 'components/Dash/SaveChangesAlert' + import { Content, Preview, Wrapper } from './styles' +import type { Data } from 'types/Data' + type DashProps = { initialData: Data } diff --git a/src/components/Home/index.tsx b/src/components/Home/index.tsx index 6aa10d5..b72106e 100644 --- a/src/components/Home/index.tsx +++ b/src/components/Home/index.tsx @@ -1,7 +1,9 @@ -import { useData } from '../../hooks/useData' -import { Avatar } from '../Avatar' -import { Button } from '../Button' -import { SocialLinks } from '../SocialLinks' +import { fonts } from 'constants/fonts' +import { useData } from 'hooks/useData' +import { Button } from 'components/Button' +import { Avatar } from 'components/Avatar' +import { SocialLinks } from 'components/SocialLinks' + import { Content, Description, Name, Wrapper } from './styles' export const Home = () => { @@ -9,7 +11,7 @@ export const Home = () => { const { colors, buttonBorderRadius, font, name, description } = data.settings return ( - + {name} diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx new file mode 100644 index 0000000..0f74734 --- /dev/null +++ b/src/components/Layout/index.tsx @@ -0,0 +1,25 @@ +import Head from 'next/head' + +import { fonts } from 'constants/fonts' +import { useData } from 'hooks/useData' +import { Global } from 'styles/global' + +import type { ReactNode } from 'react' + +type LayoutProps = { + children: ReactNode +} + +export function Layout({ children }: LayoutProps) { + const { data } = useData() + + return ( + <> + + + + + {children} + + ) +} diff --git a/src/components/SocialLinks/index.tsx b/src/components/SocialLinks/index.tsx index 3067462..e01d135 100644 --- a/src/components/SocialLinks/index.tsx +++ b/src/components/SocialLinks/index.tsx @@ -5,12 +5,14 @@ import { } from 'react-icons/ai' import { BsEnvelope as Email } from 'react-icons/bs' import { FaDev as Dev, FaLinkedinIn as LinkedIn } from 'react-icons/fa' -import type { IconType } from 'react-icons/lib' -import { useData } from '../../hooks/useData' -import { Link } from '../Link' +import { useData } from 'hooks/useData' +import { Link } from 'components/Link' + import { SocialItem, Wrapper } from './styles' +import type { IconType } from 'react-icons/lib' + const Icons = { Email, GitHub, diff --git a/src/constants/auth.ts b/src/constants/auth.ts new file mode 100644 index 0000000..8e63ec3 --- /dev/null +++ b/src/constants/auth.ts @@ -0,0 +1,15 @@ +const JWT_EXPIRES_IN = 15 // minutes +const SESSION_EXPIRES_IN = 30 // days + +const { JWT_SECRET } = process.env +const SESSION_EXPIRES_IN_S = SESSION_EXPIRES_IN * 24 * 60 * 60 +const SESSION_EXPIRES_IN_MS = SESSION_EXPIRES_IN_S * 1000 + +if (!JWT_SECRET && !process.browser) throw new Error('JWT_SECRET is not defined') + +export const authConstants = { + JWT_SECRET, + JWT_EXPIRES_IN, + SESSION_EXPIRES_IN_S, + SESSION_EXPIRES_IN_MS +} diff --git a/src/styles/colors.ts b/src/constants/colors.ts similarity index 100% rename from src/styles/colors.ts rename to src/constants/colors.ts diff --git a/src/constants/fonts.ts b/src/constants/fonts.ts new file mode 100644 index 0000000..fa6eddc --- /dev/null +++ b/src/constants/fonts.ts @@ -0,0 +1,30 @@ +export const fonts = { + roboto: { + name: "'Roboto', sans-serif", + url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap' + }, + robotoSlab: { + name: "'Roboto Slab', serif", + url: 'https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@400;500&display=swap' + }, + oswald: { + name: "'Oswald', sans-serif", + url: 'https://fonts.googleapis.com/css2?family=Oswald:wght@400;500&display=swap' + }, + poppins: { + name: "'Poppins', sans-serif", + url: 'https://fonts.googleapis.com/css2?family=Poppins:wght@400;500&display=swap' + }, + ubuntu: { + name: "'Ubuntu', sans-serif", + url: 'https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;500&display=swap' + }, + quicksand: { + name: "'Quicksand', sans-serif", + url: 'https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500&display=swap' + }, + inconsolata: { + name: "'Inconsolata', monospace", + url: 'https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;500&display=swap' + } +} diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index 9816a5d..00ec490 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -1,17 +1,16 @@ -import type { User } from '@prisma/client' import { useRouter } from 'next/router' import { parseCookies } from 'nookies' -import type { ReactNode } from 'react' -import { useEffect } from 'react' -import { createContext, useState } from 'react' +import { useEffect, createContext, useState } from 'react' -import { api } from '../services/api' -import { destroyCookies } from '../utils/destroyCookies' -import { retrieveUser } from '../utils/retrieveUser' -import { setCookies } from '../utils/setCookies' +import { api } from 'services/api' +import { destroyCookies, setCookies } from 'utils/cookies' +import { retrieveUser } from 'utils/retrieveUser' + +import type { ReactNode } from 'react' +import type { User } from 'types/User' type AuthContextType = { - user?: Omit + user?: User signIn: (username: string, password: string) => Promise signOut: () => void } @@ -23,7 +22,7 @@ type AuthProviderProps = { export const AuthContext = createContext({} as AuthContextType) export const AuthProvider = ({ children }: AuthProviderProps) => { - const [user, setUser] = useState>() + const [user, setUser] = useState() const { push } = useRouter() useEffect(() => { diff --git a/src/contexts/DataContext.tsx b/src/contexts/DataContext.tsx index 8d688e0..cd9cbc1 100644 --- a/src/contexts/DataContext.tsx +++ b/src/contexts/DataContext.tsx @@ -1,7 +1,7 @@ -import type { Dispatch, ReactNode, SetStateAction } from 'react' import { createContext, useState } from 'react' -import type { Data } from '../types/Data' +import type { Dispatch, ReactNode, SetStateAction } from 'react' +import type { Data } from 'types/Data' type DataContextType = { data: Data diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts index 4f0a8b9..5bf8076 100644 --- a/src/hooks/useAuth.ts +++ b/src/hooks/useAuth.ts @@ -1,5 +1,5 @@ import { useContext } from 'react' -import { AuthContext } from '../contexts/AuthContext' +import { AuthContext } from 'contexts/AuthContext' export const useAuth = () => useContext(AuthContext) diff --git a/src/hooks/useData.ts b/src/hooks/useData.ts index a02ab03..643545f 100644 --- a/src/hooks/useData.ts +++ b/src/hooks/useData.ts @@ -1,5 +1,5 @@ import { useContext } from 'react' -import { DataContext } from '../contexts/DataContext' +import { DataContext } from 'contexts/DataContext' export const useData = () => useContext(DataContext) diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index b0bc4b1..5d84023 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,8 +1,8 @@ -import type { AppProps } from 'next/app' +import { Layout } from 'components/Layout' +import { AuthProvider } from 'contexts/AuthContext' +import { DataProvider } from 'contexts/DataContext' -import { AuthProvider } from '../contexts/AuthContext' -import { DataProvider } from '../contexts/DataContext' -import { Layout } from '../styles/layout' +import type { AppProps } from 'next/app' export default function App({ Component, pageProps }: AppProps) { return ( diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index f7411ad..369a0b0 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -7,10 +7,6 @@ export default class Document extends NextDocument { -
diff --git a/src/pages/api/auth/index.ts b/src/pages/api/auth/index.ts index 55d9b7d..9a7e570 100644 --- a/src/pages/api/auth/index.ts +++ b/src/pages/api/auth/index.ts @@ -1,35 +1,11 @@ -import { Prisma } from '@prisma/client' import { compare } from 'bcryptjs' -import type { NextApiRequest, NextApiResponse } from 'next' -import nc from 'next-connect' -import { prisma } from '../../../services/prisma' -import { createSession } from '../../../utils/createSession' -import { ExceptionError } from '../../../utils/error' +import { prisma } from 'services/prisma' +import { createSession } from 'utils/createSession' +import { ExceptionError } from 'utils/error' +import { nc } from 'utils/nc' -const handler = nc({ - onNoMatch: (_req, res) => { - res.status(404).json({ error: 'Not found' }) - }, - onError: (err, _req, res) => { - if (err instanceof ExceptionError) { - res.status(err.status).json({ - status: err.status, - error: err.message - }) - } else if (err instanceof Prisma.PrismaClientKnownRequestError) { - res.status(400).json({ - status: 400, - error: err.message - }) - } else { - res.status(500).json({ - status: 500, - error: err.message - }) - } - } -}).post(async (req, res) => { +const handler = nc.post(async (req, res) => { const { username, password } = req.body if (!username || !password) { diff --git a/src/pages/api/auth/refresh-token.ts b/src/pages/api/auth/refresh-token.ts index 70e6376..66cd3a6 100644 --- a/src/pages/api/auth/refresh-token.ts +++ b/src/pages/api/auth/refresh-token.ts @@ -1,34 +1,9 @@ -import { Prisma } from '@prisma/client' -import type { NextApiRequest, NextApiResponse } from 'next' -import nc from 'next-connect' +import { prisma } from 'services/prisma' +import { createSession } from 'utils/createSession' +import { ExceptionError } from 'utils/error' +import { nc } from 'utils/nc' -import { prisma } from '../../../services/prisma' -import { createSession } from '../../../utils/createSession' -import { ExceptionError } from '../../../utils/error' - -const handler = nc({ - onNoMatch: (_req, res) => { - res.status(404).json({ error: 'Not found' }) - }, - onError: (err, _req, res) => { - if (err instanceof ExceptionError) { - res.status(err.status).json({ - status: err.status, - error: err.message - }) - } else if (err instanceof Prisma.PrismaClientKnownRequestError) { - res.status(400).json({ - status: 400, - error: err.message - }) - } else { - res.status(500).json({ - status: 500, - error: err.message - }) - } - } -}).post(async (req, res) => { +const handler = nc.post(async (req, res) => { const { refreshToken } = req.body if (!refreshToken) { diff --git a/src/pages/api/data.ts b/src/pages/api/data.ts index 3277c3b..c3998fa 100644 --- a/src/pages/api/data.ts +++ b/src/pages/api/data.ts @@ -1,34 +1,9 @@ -import { Prisma } from '@prisma/client' -import type { NextApiRequest, NextApiResponse } from 'next' -import nc from 'next-connect' - -import { prisma } from '../../services/prisma' +import { prisma } from 'services/prisma' // import { authMiddleware } from '../../utils/authMiddleware' -import { ExceptionError } from '../../utils/error' +import { ExceptionError } from 'utils/error' +import { nc } from 'utils/nc' -const handler = nc({ - onNoMatch: (_req, res) => { - res.status(404).json({ error: 'Not found' }) - }, - onError: (err, _req, res) => { - if (err instanceof ExceptionError) { - res.status(err.status).json({ - status: err.status, - error: err.message - }) - } else if (err instanceof Prisma.PrismaClientKnownRequestError) { - res.status(400).json({ - status: 400, - error: err.message - }) - } else { - res.status(500).json({ - status: 500, - error: err.message - }) - } - } -}) +const handler = nc .get(async (_req, res) => { const response = await prisma.data.findUnique({ where: { id: 1 } diff --git a/src/pages/api/users/[id].ts b/src/pages/api/users/[id].ts index 3a88c43..9cbf124 100644 --- a/src/pages/api/users/[id].ts +++ b/src/pages/api/users/[id].ts @@ -1,39 +1,10 @@ -import { Prisma } from '@prisma/client' import { compare, hashSync } from 'bcryptjs' -import type { NextApiRequest, NextApiResponse } from 'next' -import nc from 'next-connect' -import { prisma } from '../../../services/prisma' -import { ExceptionError } from '../../../utils/error' +import { nc } from 'utils/nc' +import { prisma } from 'services/prisma' +import { ExceptionError } from 'utils/error' -interface Request extends NextApiRequest { - userId: string - userRole: string -} - -const handler = nc({ - onNoMatch: (_req, res) => { - res.status(404).json({ error: 'Not found' }) - }, - onError: (err, _req, res) => { - if (err instanceof ExceptionError) { - res.status(err.status).json({ - status: err.status, - error: err.message - }) - } else if (err instanceof Prisma.PrismaClientKnownRequestError) { - res.status(400).json({ - status: 400, - error: err.message - }) - } else { - res.status(500).json({ - status: 500, - error: err.message - }) - } - } -}) +const handler = nc .get(async (req, res) => { const { id } = req.query diff --git a/src/pages/api/users/index.ts b/src/pages/api/users/index.ts index 33c6b08..3c68a02 100644 --- a/src/pages/api/users/index.ts +++ b/src/pages/api/users/index.ts @@ -1,40 +1,11 @@ -import { Prisma } from '@prisma/client' import { hashSync } from 'bcryptjs' -import type { NextApiRequest, NextApiResponse } from 'next' -import nc from 'next-connect' -import { prisma } from '../../../services/prisma' -import { authMiddleware } from '../../../utils/authMiddleware' -import { ExceptionError } from '../../../utils/error' +import { prisma } from 'services/prisma' +import { authMiddleware } from 'utils/authMiddleware' +import { ExceptionError } from 'utils/error' +import { nc } from 'utils/nc' -interface Request extends NextApiRequest { - userId: string - userRole: string -} - -const handler = nc({ - onNoMatch: (_req, res) => { - res.status(404).json({ error: 'Not found' }) - }, - onError: (err, _req, res) => { - if (err instanceof ExceptionError) { - res.status(err.status).json({ - status: err.status, - error: err.message - }) - } else if (err instanceof Prisma.PrismaClientKnownRequestError) { - res.status(400).json({ - status: 400, - error: err.message - }) - } else { - res.status(500).json({ - status: 500, - error: err.message - }) - } - } -}) +const handler = nc .use(authMiddleware) .get(async (_req, res) => { const response = await prisma.user.findMany({ diff --git a/src/pages/api/users/me.ts b/src/pages/api/users/me.ts index 8f2f671..4ec280f 100644 --- a/src/pages/api/users/me.ts +++ b/src/pages/api/users/me.ts @@ -1,56 +1,24 @@ -import { Prisma } from '@prisma/client' -import type { NextApiRequest, NextApiResponse } from 'next' -import nc from 'next-connect' +import { prisma } from 'services/prisma' +import { authMiddleware } from 'utils/authMiddleware' +import { ExceptionError } from 'utils/error' +import { nc } from 'utils/nc' -import { prisma } from '../../../services/prisma' -import { authMiddleware } from '../../../utils/authMiddleware' -import { ExceptionError } from '../../../utils/error' - -interface Request extends NextApiRequest { - userId: string - userRole: string -} - -const handler = nc({ - onNoMatch: (_req, res) => { - res.status(404).json({ error: 'Not found' }) - }, - onError: (err, _req, res) => { - if (err instanceof ExceptionError) { - res.status(err.status).json({ - status: err.status, - error: err.message - }) - } else if (err instanceof Prisma.PrismaClientKnownRequestError) { - res.status(400).json({ - status: 400, - error: err.message - }) - } else { - res.status(500).json({ - status: 500, - error: err.message - }) +const handler = nc.use(authMiddleware).get(async (req, res) => { + const { userId } = req + const response = await prisma.user.findUnique({ + where: { id: +userId }, + select: { + id: true, + username: true, + role: true } - } -}) - .use(authMiddleware) - .get(async (req, res) => { - const { userId } = req - const response = await prisma.user.findUnique({ - where: { id: +userId }, - select: { - id: true, - username: true, - role: true - } - }) - - if (!response) { - throw new ExceptionError('User not found') - } - - res.status(200).json(response) }) + if (!response) { + throw new ExceptionError('User not found') + } + + res.status(200).json(response) +}) + export default handler diff --git a/src/pages/auth.tsx b/src/pages/auth.tsx index 05d5691..ca1f0ba 100644 --- a/src/pages/auth.tsx +++ b/src/pages/auth.tsx @@ -1,8 +1,9 @@ -import type { GetServerSideProps } from 'next' import { useState } from 'react' -import { useAuth } from '../hooks/useAuth' -import { fetchData } from '../services/fetchData' +import { useAuth } from 'hooks/useAuth' +import { fetchData } from 'services/fetchData' + +import type { GetStaticProps } from 'next' export default function AuthPage() { const { signIn } = useAuth() @@ -35,10 +36,7 @@ export default function AuthPage() { ) } -export const getServerSideProps: GetServerSideProps = async () => { +export const getStaticProps: GetStaticProps = async () => { const initialData = await fetchData() - - return { - props: { initialData } - } + return { props: { initialData }, revalidate: 1 } } diff --git a/src/pages/dash.tsx b/src/pages/dash.tsx index 2838241..2888e2b 100644 --- a/src/pages/dash.tsx +++ b/src/pages/dash.tsx @@ -1,8 +1,8 @@ -import type { GetServerSideProps } from 'next' +import { Dash } from 'components/Dash' +import { fetchData } from 'services/fetchData' -import { Dash } from '../components/Dash' -import { fetchData } from '../services/fetchData' -import type { Data } from '../types/Data' +import type { GetServerSideProps } from 'next' +import type { Data } from 'types/Data' type DashPageProps = { initialData: Data @@ -14,8 +14,5 @@ export default function DashPage({ initialData }: DashPageProps) { export const getServerSideProps: GetServerSideProps = async () => { const initialData = await fetchData() - - return { - props: { initialData } - } + return { props: { initialData } } } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 4b9fd2a..b34ad17 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,7 +1,7 @@ -import type { GetStaticProps } from 'next' +import { Home } from 'components/Home' +import { fetchData } from 'services/fetchData' -import { Home } from '../components/Home' -import { fetchData } from '../services/fetchData' +import type { GetStaticProps } from 'next' export default function HomePage() { return @@ -9,9 +9,5 @@ export default function HomePage() { export const getStaticProps: GetStaticProps = async () => { const initialData = await fetchData() - - return { - props: { initialData }, - revalidate: 1 - } + return { props: { initialData }, revalidate: 1 } } diff --git a/src/services/api.ts b/src/services/api.ts index 3b855f2..487c024 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -3,8 +3,7 @@ import { GetServerSidePropsContext } from 'next' import router from 'next/router' import { parseCookies } from 'nookies' -import { destroyCookies } from '../utils/destroyCookies' -import { setCookies } from '../utils/setCookies' +import { destroyCookies, setCookies } from 'utils/cookies' let isRefreshing = false let failedRequestsQueued: any[] = [] diff --git a/src/services/fetchData.ts b/src/services/fetchData.ts index 11e69ec..03fb6ed 100644 --- a/src/services/fetchData.ts +++ b/src/services/fetchData.ts @@ -1,5 +1,6 @@ -import type { Data } from '../types/Data' -import { prisma } from './prisma' +import { prisma } from 'services/prisma' + +import type { Data } from 'types/Data' export async function fetchData(): Promise { const response = await prisma.data.findUnique({ diff --git a/src/styles/global.ts b/src/styles/global.ts new file mode 100644 index 0000000..5082660 --- /dev/null +++ b/src/styles/global.ts @@ -0,0 +1,22 @@ +import { createGlobalStyle } from 'styled-components' + +import type { Data } from 'types/Data' + +type GlobalStyleProps = { + data: Data +} + +export const Global = createGlobalStyle` + * { + margin: 0; + padding: 0; + box-sizing: border-box; + transition: all 0.25s ease; + } + + body { + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + color: #0f172a; + background: ${({ data }) => data.settings.colors.background}; + } +` diff --git a/src/styles/layout.tsx b/src/styles/layout.tsx deleted file mode 100644 index 6641757..0000000 --- a/src/styles/layout.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { ReactNode } from 'react' -import { createGlobalStyle } from 'styled-components' - -import { useData } from '../hooks/useData' -import { Data } from '../types/Data' - -type GlobalStyleProps = { - data: Data -} - -type LayoutProps = { - children: ReactNode -} - -const GlobalStyles = createGlobalStyle` - * { - margin: 0; - padding: 0; - box-sizing: border-box; - transition: all 0.25s ease; - } - - body { - font-family: "Roboto", sans-serif; - color: #0f172a; - background: ${({ data }) => data.settings.colors.background}; - } -` - -export function Layout({ children }: LayoutProps) { - const { data } = useData() - - return ( - <> - - {children} - - ) -} diff --git a/src/types/Data.ts b/src/types/Data.ts index faa2d69..86e0741 100644 --- a/src/types/Data.ts +++ b/src/types/Data.ts @@ -1,9 +1,11 @@ +import type { Fonts } from './Fonts' + export type Data = { settings: { avatar: string name: string description: string - font: string + font: Fonts buttonBorderRadius: string colors: { texts: string diff --git a/src/types/Fonts.ts b/src/types/Fonts.ts new file mode 100644 index 0000000..3df3517 --- /dev/null +++ b/src/types/Fonts.ts @@ -0,0 +1,2 @@ +import { fonts } from 'constants/fonts' +export type Fonts = keyof typeof fonts diff --git a/src/types/User.ts b/src/types/User.ts new file mode 100644 index 0000000..e22d245 --- /dev/null +++ b/src/types/User.ts @@ -0,0 +1,2 @@ +import { User as UserModel } from '@prisma/client' +export type User = Omit diff --git a/src/types/next.d.ts b/src/types/next.d.ts new file mode 100644 index 0000000..c5cdde4 --- /dev/null +++ b/src/types/next.d.ts @@ -0,0 +1,8 @@ +import { IncomingMessage } from 'http' + +declare module 'next' { + export interface NextApiRequest extends IncomingMessage { + userId: string + userRole: string + } +} diff --git a/src/utils/authMiddleware.ts b/src/utils/authMiddleware.ts index 87d6608..be52297 100644 --- a/src/utils/authMiddleware.ts +++ b/src/utils/authMiddleware.ts @@ -1,19 +1,17 @@ import jwt from 'jsonwebtoken' + +import { authConstants } from 'constants/auth' +import { ExceptionError } from 'utils/error' + import type { NextApiRequest, NextApiResponse } from 'next' -import { ExceptionError } from './error' - -interface Request extends NextApiRequest { - userId: string - userRole: string -} - type Token = { sub: string role: string } -export async function authMiddleware(req: Request, _res: NextApiResponse, next: () => void) { +export async function authMiddleware(req: NextApiRequest, _res: NextApiResponse, next: () => void) { + const { JWT_SECRET } = authConstants const token = req.headers['authorization'] if (!token) { @@ -22,13 +20,7 @@ export async function authMiddleware(req: Request, _res: NextApiResponse, next: const [, tokenValue] = token.split(' ') - const { JWT_SECRET } = process.env - - if (!JWT_SECRET) { - throw new ExceptionError('JWT_SECRET is not defined') - } - - const isValidToken = jwt.verify(tokenValue, JWT_SECRET) as Token + const isValidToken = jwt.verify(tokenValue, JWT_SECRET!) as Token if (!isValidToken) { throw new ExceptionError('Invalid token', 401) diff --git a/src/utils/cookies.ts b/src/utils/cookies.ts new file mode 100644 index 0000000..55c293d --- /dev/null +++ b/src/utils/cookies.ts @@ -0,0 +1,38 @@ +import { destroyCookie, setCookie } from 'nookies' + +import { authConstants } from 'constants/auth' +import { api } from 'services/api' + +import type { GetServerSidePropsContext } from 'next' + +type SetCookiesProps = { + ctx?: GetServerSidePropsContext + accessToken: string + refreshToken: string +} + +export function setCookies({ ctx = undefined, accessToken, refreshToken }: SetCookiesProps) { + const { SESSION_EXPIRES_IN_S } = authConstants + // @ts-ignore + api.defaults.headers['authorization'] = `Bearer ${accessToken}` + + setCookie(ctx, 'accessToken', accessToken, { + maxAge: SESSION_EXPIRES_IN_S, + path: '/' + }) + + setCookie(ctx, 'refreshToken', refreshToken, { + maxAge: SESSION_EXPIRES_IN_S, + path: '/' + }) +} + +export function destroyCookies(ctx: GetServerSidePropsContext | undefined = undefined) { + destroyCookie(ctx, 'accessToken', { + path: '/' + }) + + destroyCookie(ctx, 'refreshToken', { + path: '/' + }) +} diff --git a/src/utils/createSession.ts b/src/utils/createSession.ts index e3fb89c..68859bf 100644 --- a/src/utils/createSession.ts +++ b/src/utils/createSession.ts @@ -1,30 +1,25 @@ -import type { User } from '@prisma/client' import jwt from 'jsonwebtoken' -import { prisma } from '../services/prisma' -import { ExceptionError } from './error' +import { authConstants } from 'constants/auth' +import { prisma } from 'services/prisma' +import { ExceptionError } from 'utils/error' -const EXPIRES_IN = 30 // days -const EXPIRES_IN_MS = EXPIRES_IN * 24 * 60 * 60 * 1000 +import type { User } from '@prisma/client' export async function createSession(user: User) { - const { JWT_SECRET } = process.env - - if (!JWT_SECRET) { - throw new ExceptionError('JWT_SECRET is not defined') - } + const { SESSION_EXPIRES_IN_MS, JWT_EXPIRES_IN, JWT_SECRET } = authConstants try { const session = await prisma.session.create({ data: { userId: user.id, - expiresAt: new Date(Date.now() + EXPIRES_IN_MS) + expiresAt: new Date(Date.now() + SESSION_EXPIRES_IN_MS) } }) const payload = { role: user.role, sub: user.id } - const accessToken = jwt.sign(payload, JWT_SECRET, { - expiresIn: '15m' + const accessToken = jwt.sign(payload, JWT_SECRET!, { + expiresIn: `${JWT_EXPIRES_IN}m` }) return { accessToken, refreshToken: session.id } diff --git a/src/utils/destroyCookies.ts b/src/utils/destroyCookies.ts deleted file mode 100644 index 56a8821..0000000 --- a/src/utils/destroyCookies.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { GetServerSidePropsContext } from 'next' -import { destroyCookie } from 'nookies' - -export function destroyCookies(ctx: GetServerSidePropsContext | undefined = undefined) { - destroyCookie(ctx, 'accessToken', { - path: '/' - }) - - destroyCookie(ctx, 'refreshToken', { - path: '/' - }) -} diff --git a/src/utils/localStorage.ts b/src/utils/localStorage.ts index e793b21..e0dd722 100644 --- a/src/utils/localStorage.ts +++ b/src/utils/localStorage.ts @@ -1,4 +1,4 @@ -import type { Data } from '../types/Data' +import type { Data } from 'types/Data' const KEY = 'link-free-data' @@ -8,9 +8,9 @@ export function getLocalStorageData(): Data | null { } export function setLocalStorageData(data = {}) { - localStorage.setItem(KEY, JSON.stringify(data)) + process.browser && localStorage.setItem(KEY, JSON.stringify(data)) } export function removeLocalStorageData() { - localStorage.removeItem(KEY) + process.browser && localStorage.removeItem(KEY) } diff --git a/src/utils/nc.ts b/src/utils/nc.ts new file mode 100644 index 0000000..2af7b89 --- /dev/null +++ b/src/utils/nc.ts @@ -0,0 +1,30 @@ +import nextConnect from 'next-connect' +import { Prisma } from '@prisma/client' + +import { ExceptionError } from 'utils/error' + +import type { NextApiRequest, NextApiResponse } from 'next' + +export const nc = nextConnect({ + onNoMatch: (_req, res) => { + res.status(404).json({ error: 'Not found' }) + }, + onError: (err, _req, res) => { + if (err instanceof ExceptionError) { + res.status(err.status).json({ + status: err.status, + error: err.message + }) + } else if (err instanceof Prisma.PrismaClientKnownRequestError) { + res.status(400).json({ + status: 400, + error: err.message + }) + } else { + res.status(500).json({ + status: 500, + error: err.message + }) + } + } +}) diff --git a/src/utils/retrieveUser.ts b/src/utils/retrieveUser.ts index e7b54f7..9568305 100644 --- a/src/utils/retrieveUser.ts +++ b/src/utils/retrieveUser.ts @@ -1,10 +1,10 @@ -import type { User } from '@prisma/client' +import { api } from 'services/api' -import { api } from '../services/api' +import type { User } from 'types/User' export async function retrieveUser() { try { - const { data } = await api.get>('users/me') + const { data } = await api.get('users/me') return data } catch (error) { console.log(error) diff --git a/src/utils/setCookies.ts b/src/utils/setCookies.ts deleted file mode 100644 index 4470cbe..0000000 --- a/src/utils/setCookies.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { GetServerSidePropsContext } from 'next' -import { setCookie } from 'nookies' - -import { api } from '../services/api' - -type SetCookiesProps = { - ctx?: GetServerSidePropsContext - accessToken: string - refreshToken: string -} - -export function setCookies({ ctx = undefined, accessToken, refreshToken }: SetCookiesProps) { - // @ts-ignore - api.defaults.headers['authorization'] = `Bearer ${accessToken}` - - setCookie(ctx, 'accessToken', accessToken, { - maxAge: 60 * 60 * 24 * 30, // 30 days - path: '/' - }) - - setCookie(ctx, 'refreshToken', refreshToken, { - maxAge: 60 * 60 * 24 * 30, // 30 days - path: '/' - }) -} diff --git a/tsconfig.json b/tsconfig.json index 99710e8..ca2f530 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "baseUrl": "./src", "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, diff --git a/yarn.lock b/yarn.lock index a2cbd08..b24a5d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -535,7 +535,7 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@prisma/client@^3.6.0": +"@prisma/client@3.6.0": version "3.6.0" resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.6.0.tgz#68a60cd4c73a369b11f72e173e86fd6789939293" integrity sha512-ycSGY9EZGROtje0iCNsgC5Zqi/ttX2sO7BNMYaLsUMiTlf3F69ZPH+08pRo0hrDfkZzyimXYqeXJlaoYDH1w7A== @@ -577,7 +577,7 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== -"@types/bcryptjs@^2.4.2": +"@types/bcryptjs@2.4.2": version "2.4.2" resolved "https://registry.yarnpkg.com/@types/bcryptjs/-/bcryptjs-2.4.2.tgz#e3530eac9dd136bfdfb0e43df2c4c5ce1f77dfae" integrity sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ== @@ -595,14 +595,14 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/jsonwebtoken@^8.5.6": +"@types/jsonwebtoken@8.5.6": version "8.5.6" resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.6.tgz#1913e5a61e70a192c5a444623da4901a7b1a9d42" integrity sha512-+P3O/xC7nzVizIi5VbF34YtqSonFsdnbXBnWUCYRiKOi1f9gA4sEFvXkrGr/QVV23IbMYvcoerI7nnhDUiWXRQ== dependencies: "@types/node" "*" -"@types/lodash@^4.14.178": +"@types/lodash@4.14.178": version "4.14.178" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8" integrity sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw== @@ -622,7 +622,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.12.tgz#ac7fb693ac587ee182c3780c26eb65546a1a3c10" integrity sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw== -"@types/node@^17.0.0": +"@types/node@17.0.0": version "17.0.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.0.tgz#62797cee3b8b497f6547503b2312254d4fe3c2bb" integrity sha512-eMhwJXc931Ihh4tkU+Y7GiLzT/y/DBNpNtr4yU9O2w3SYBsr9NaOPhQlLKRmoWtI54uNwuo0IOUFQjVOTZYRvw== @@ -642,7 +642,7 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== -"@types/react-color@^3.0.6": +"@types/react-color@3.0.6": version "3.0.6" resolved "https://registry.yarnpkg.com/@types/react-color/-/react-color-3.0.6.tgz#602fed023802b2424e7cd6ff3594ccd3d5055f9a" integrity sha512-OzPIO5AyRmLA7PlOyISlgabpYUa3En74LP8mTMa0veCA719SvYQov4WLMsHvCgXP+L+KI9yGhYnqZafVGG0P4w== @@ -671,7 +671,7 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== -"@types/styled-components@^5.1.18": +"@types/styled-components@5.1.18": version "5.1.18" resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.18.tgz#1c361130f76a52f1cb4851c2a32aa328267e5b78" integrity sha512-xPTYmWP7Mxk5TAD3pYsqjwA9G5fAI8e/S51QUJEl7EQD1siKCdiYXIWiH2lzoHRl+QqbQCJMcGv3YTF3OmyPdQ== @@ -954,7 +954,7 @@ axe-core@^4.3.5: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.3.5.tgz#78d6911ba317a8262bfee292aeafcc1e04b49cc5" integrity sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA== -axios@^0.24.0: +axios@0.24.0: version "0.24.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== @@ -966,7 +966,7 @@ axobject-query@^2.2.0: resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== -"babel-plugin-styled-components@>= 1.12.0", babel-plugin-styled-components@^2.0.2: +babel-plugin-styled-components@2.0.2, "babel-plugin-styled-components@>= 1.12.0": version "2.0.2" resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.2.tgz#0fac11402dc9db73698b55847ab1dc73f5197c54" integrity sha512-7eG5NE8rChnNTDxa6LQfynwgHTVOYYaHJbUYSlOhk8QBXIQiMBKq4gyfHBBKPrxUcVBXVJL61ihduCpCQbuNbw== @@ -991,7 +991,7 @@ base64-js@^1.0.2, base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -bcryptjs@^2.4.3: +bcryptjs@2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms= @@ -1553,7 +1553,7 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5" integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA== -cuid@^2.1.8: +cuid@2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/cuid/-/cuid-2.1.8.tgz#cbb88f954171e0d5747606c0139fb65c5101eac0" integrity sha512-xiEMER6E7TlTPnDxrM4eRiC6TRgjNX9xzEZ5U/Se2YJKr7Mq4pJn/2XEHjl3STcSh96GmkHPcBXLES8M29wyyg== @@ -1706,7 +1706,7 @@ dot-prop@^5.1.0: dependencies: is-obj "^2.0.0" -dotenv@^10.0.0: +dotenv@10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== @@ -1882,7 +1882,7 @@ eslint-module-utils@^2.7.1: find-up "^2.1.0" pkg-dir "^2.0.0" -eslint-plugin-import@^2.25.2: +eslint-plugin-import@2.25.3, eslint-plugin-import@^2.25.2: version "2.25.3" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.3.tgz#a554b5f66e08fb4f6dc99221866e57cfff824766" integrity sha512-RzAVbby+72IB3iOEL8clzPLzL3wpDrlwjsTBAQXgyp5SeTqqY+0bFubwuo+y/HLhNZcXV4XqTBO4LGsfyHIDXg== @@ -1951,11 +1951,6 @@ eslint-plugin-react@^7.27.0: semver "^6.3.0" string.prototype.matchall "^4.0.6" -eslint-plugin-simple-import-sort@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz#a1dad262f46d2184a90095a60c66fef74727f0f8" - integrity sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw== - eslint-scope@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.0.tgz#c1f6ea30ac583031f203d65c73e723b01298f153" @@ -2867,7 +2862,7 @@ jsonparse@^1.2.0: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= -jsonwebtoken@^8.5.1: +jsonwebtoken@8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== @@ -3062,7 +3057,7 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: +lodash@4.17.21, lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -3267,7 +3262,7 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -next-connect@^0.11.0: +next-connect@0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/next-connect/-/next-connect-0.11.0.tgz#447c7b78937ff71f36c97d03f1623a6d0264ef49" integrity sha512-7VW5z9MWOpd8wnbST9dXgVd0c0V76LLLk5mBDYLSMtEC2dj3v3YF/Bl3t58cJrzm4SZ0e/s3yeD361F888u0yg== @@ -3370,7 +3365,7 @@ node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.77.tgz#50b0cfede855dd374e7585bf228ff34e57c1c32e" integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ== -nookies@^2.5.2: +nookies@2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/nookies/-/nookies-2.5.2.tgz#cc55547efa982d013a21475bd0db0c02c1b35b27" integrity sha512-x0TRSaosAEonNKyCrShoUaJ5rrT5KHRNZ5DwPCuizjgrnkpE5DRf3VL7AyyQin4htict92X1EQ7ejDbaHDVdYA== @@ -3691,7 +3686,7 @@ platform@1.3.6: resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== -polished@^4.1.3: +polished@4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/polished/-/polished-4.1.3.tgz#7a3abf2972364e7d97770b827eec9a9e64002cfc" integrity sha512-ocPAcVBUOryJEKe0z2KLd1l9EBa1r5mSwlKpExmrLzsnIzJo4axsoU9O2BjOTkDGDT4mZ0WFE5XKTlR3nLnZOA== @@ -3760,7 +3755,7 @@ pretty-quick@3.1.2: mri "^1.1.5" multimatch "^4.0.0" -prisma@^3.6.0: +prisma@3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.6.0.tgz#99532abc02e045e58c6133a19771bdeb28cecdbe" integrity sha512-6SqgHS/5Rq6HtHjsWsTxlj+ySamGyCLBUQfotc2lStOjPv52IQuDVpp58GieNqc9VnfuFyHUvTZw7aQB+G2fvQ== @@ -3889,7 +3884,7 @@ rc-motion@^2.0.0: classnames "^2.2.1" rc-util "^5.2.1" -rc-slider@^9.7.5: +rc-slider@9.7.5: version "9.7.5" resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-9.7.5.tgz#193141c68e99b1dc3b746daeb6bf852946f5b7f4" integrity sha512-LV/MWcXFjco1epPbdw1JlLXlTgmWpB9/Y/P2yinf8Pg3wElHxA9uajN21lJiWtZjf5SCUekfSP6QMJfDo4t1hg== @@ -3938,12 +3933,12 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-collapsible@^2.8.4: +react-collapsible@2.8.4: version "2.8.4" resolved "https://registry.yarnpkg.com/react-collapsible/-/react-collapsible-2.8.4.tgz#319ff7471138c4381ce0afa3ac308ccde7f4e09f" integrity sha512-oG4yOk6AGKswe0OD/8t3/nf4Rgj4UhlZUUvqL5jop0/ez02B3dBDmNvs3sQz0PcTpJvt0ai8zF7Atd1SzN/UNw== -react-color@^2.19.3: +react-color@2.19.3: version "2.19.3" resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.19.3.tgz#ec6c6b4568312a3c6a18420ab0472e146aa5683d" integrity sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA== @@ -3956,7 +3951,7 @@ react-color@^2.19.3: reactcss "^1.2.0" tinycolor2 "^1.4.1" -react-dom@^17.0.2: +react-dom@17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== @@ -3965,12 +3960,12 @@ react-dom@^17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" -react-icons@^4.3.1: +react-icons@4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.3.1.tgz#2fa92aebbbc71f43d2db2ed1aed07361124e91ca" integrity sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ== -react-is@17.0.2, react-is@^17.0.2: +react-is@17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== @@ -3985,7 +3980,7 @@ react-refresh@0.8.3: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg== -react@^17.0.2: +react@17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== @@ -4247,7 +4242,7 @@ shallowequal@^1.1.0: resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== -sharp@^0.29.3: +sharp@0.29.3: version "0.29.3" resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.29.3.tgz#0da183d626094c974516a48fab9b3e4ba92eb5c2" integrity sha512-fKWUuOw77E4nhpyzCCJR1ayrttHoFHBT2U/kR/qEMRhvPEcluG4BKj324+SCO1e84+knXHwhJ1HHJGnUt4ElGA== @@ -4573,7 +4568,7 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -styled-components@^5.3.3: +styled-components@5.3.3: version "5.3.3" resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.3.tgz#312a3d9a549f4708f0fb0edc829eb34bde032743" integrity sha512-++4iHwBM7ZN+x6DtPPWkCI4vdtwumQ+inA/DdAsqYd4SVgUKJie5vXyzotA00ttcFdQkCng7zc6grwlfIfw+lw== @@ -4730,7 +4725,7 @@ trouter@^3.1.0: dependencies: regexparam "^1.3.0" -ts-node@^10.4.0: +ts-node@10.4.0: version "10.4.0" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7" integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A== @@ -4841,16 +4836,16 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +typescript@4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" + integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== + typescript@^4.4.3: version "4.5.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.3.tgz#afaa858e68c7103317d89eb90c5d8906268d353c" integrity sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ== -typescript@^4.5.4: - version "4.5.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" - integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== - unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471"