refactor: some adjustments

This commit is contained in:
Daniel Soares
2021-12-19 20:03:59 -03:00
parent a9ff01aa52
commit e4b037f273
52 changed files with 417 additions and 481 deletions

View File

@@ -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"
}
}

View File

@@ -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"

View File

@@ -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',

View File

@@ -1,4 +1,5 @@
import { useData } from '../../hooks/useData'
import { useData } from 'hooks/useData'
import { Wrapper } from './styles'
export function Avatar() {

View File

@@ -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'
}))<ButtonProps>`
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;

View File

@@ -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:

View File

@@ -1,4 +1,4 @@
import { ColorPicker } from '../ColorPicker'
import { ColorPicker } from 'components/Dash/ColorPicker'
export function ColorsSettings() {
return (

View File

@@ -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()

View File

@@ -1,4 +1,4 @@
import { useData } from '../../../hooks/useData'
import { useData } from 'hooks/useData'
export function DescriptionInput() {
const { data, setData } = useData()

View File

@@ -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
}
})
}
>
<option value="'Baloo Bhaijaan 2', sans-serif">Baloo</option>
<option value="'Montserrat', sans-serif">Montserrat</option>
<option value="'Roboto', sans-serif">Roboto</option>
<option value="inconsolata">Inconsolata</option>
<option value="oswald">Oswald</option>
<option value="poppins">Poppins</option>
<option value="quicksand">Quicksand</option>
<option value="roboto">Roboto</option>
<option value="robotoSlab">Roboto Slab</option>
<option value="ubuntu">Ubuntu</option>
</select>
)
}

View File

@@ -1,4 +1,4 @@
import { useData } from '../../../hooks/useData'
import { useData } from 'hooks/useData'
export function NameInput() {
const { data, setData } = useData()

View File

@@ -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<SetStateAction<boolean>>

View File

@@ -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
}

View File

@@ -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 (
<Wrapper color={colors.texts} font={font}>
<Wrapper color={colors.texts} font={fonts[font].name}>
<Content>
<Avatar />
<Name>{name}</Name>

View File

@@ -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 (
<>
<Global data={data} />
<Head>
<link href={fonts[data.settings.font].url} rel="stylesheet" />
</Head>
{children}
</>
)
}

View File

@@ -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,

15
src/constants/auth.ts Normal file
View File

@@ -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
}

30
src/constants/fonts.ts Normal file
View File

@@ -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'
}
}

View File

@@ -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, 'password'>
user?: User
signIn: (username: string, password: string) => Promise<void>
signOut: () => void
}
@@ -23,7 +22,7 @@ type AuthProviderProps = {
export const AuthContext = createContext({} as AuthContextType)
export const AuthProvider = ({ children }: AuthProviderProps) => {
const [user, setUser] = useState<Omit<User, 'password'>>()
const [user, setUser] = useState<User>()
const { push } = useRouter()
useEffect(() => {

View File

@@ -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

View File

@@ -1,5 +1,5 @@
import { useContext } from 'react'
import { AuthContext } from '../contexts/AuthContext'
import { AuthContext } from 'contexts/AuthContext'
export const useAuth = () => useContext(AuthContext)

View File

@@ -1,5 +1,5 @@
import { useContext } from 'react'
import { DataContext } from '../contexts/DataContext'
import { DataContext } from 'contexts/DataContext'
export const useData = () => useContext(DataContext)

View File

@@ -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 (

View File

@@ -7,10 +7,6 @@ export default class Document extends NextDocument {
<Head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Baloo+Bhaijaan+2:wght@400;500&family=Montserrat:wght@400;500&family=Roboto:wght@400;500&display=swap"
rel="stylesheet"
/>
</Head>
<body>
<Main />

View File

@@ -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<NextApiRequest, NextApiResponse>({
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) {

View File

@@ -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<NextApiRequest, NextApiResponse>({
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) {

View File

@@ -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<NextApiRequest, NextApiResponse>({
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 }

View File

@@ -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<Request, NextApiResponse>({
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

View File

@@ -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<Request, NextApiResponse>({
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({

View File

@@ -1,41 +1,9 @@
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<Request, NextApiResponse>({
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
})
}
}
})
.use(authMiddleware)
.get(async (req, res) => {
const handler = nc.use(authMiddleware).get(async (req, res) => {
const { userId } = req
const response = await prisma.user.findUnique({
where: { id: +userId },

View File

@@ -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 }
}

View File

@@ -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 } }
}

View File

@@ -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 <Home />
@@ -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 }
}

View File

@@ -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[] = []

View File

@@ -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<Data> {
const response = await prisma.data.findUnique({

22
src/styles/global.ts Normal file
View File

@@ -0,0 +1,22 @@
import { createGlobalStyle } from 'styled-components'
import type { Data } from 'types/Data'
type GlobalStyleProps = {
data: Data
}
export const Global = createGlobalStyle<GlobalStyleProps>`
* {
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};
}
`

View File

@@ -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<GlobalStyleProps>`
* {
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 (
<>
<GlobalStyles data={data} />
{children}
</>
)
}

View File

@@ -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

2
src/types/Fonts.ts Normal file
View File

@@ -0,0 +1,2 @@
import { fonts } from 'constants/fonts'
export type Fonts = keyof typeof fonts

2
src/types/User.ts Normal file
View File

@@ -0,0 +1,2 @@
import { User as UserModel } from '@prisma/client'
export type User = Omit<UserModel, 'password'>

8
src/types/next.d.ts vendored Normal file
View File

@@ -0,0 +1,8 @@
import { IncomingMessage } from 'http'
declare module 'next' {
export interface NextApiRequest extends IncomingMessage {
userId: string
userRole: string
}
}

View File

@@ -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)

38
src/utils/cookies.ts Normal file
View File

@@ -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: '/'
})
}

View File

@@ -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 }

View File

@@ -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: '/'
})
}

View File

@@ -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)
}

30
src/utils/nc.ts Normal file
View File

@@ -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<NextApiRequest, NextApiResponse>({
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
})
}
}
})

View File

@@ -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<Omit<User, 'password'>>('users/me')
const { data } = await api.get<User>('users/me')
return data
} catch (error) {
console.log(error)

View File

@@ -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: '/'
})
}

View File

@@ -1,5 +1,6 @@
{
"compilerOptions": {
"baseUrl": "./src",
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,

View File

@@ -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"