Twitter-Clone(8~9)

aspalt85·2021년 3월 8일
0

Twitter-Clone

목록 보기
3/4
post-thumbnail

➰ Making Protected Routes

1. 📝Create 📁web ➡ 📁src ➡ 📁components ➡ 📃IsAuthenticated.tsx

import {gql, useQuery} from '@apollo/client'
🚗 import { Redirect } from 'react-router'

const IS_LOGGED_IN = gql`
{
    me {
        id
    }
}
`
interface Props {
    children?: React.ReactNode
}
const IsAuthenticated = ({children} : Props) => {
    const{loading, error, data} = useQuery(IS_LOGGED_IN)
    if(loading) return <p>Loading...</p>
    if(error) return <p>{error.message}</p>
    if (!data.me) {
        return <Redirect to={{pathname : '/landing'}} />
    }
    return <>{children}</>
}

export default IsAuthenticated;

📋 Memo(IsAuthenticated.tsx)

🚗 import { Redirect } from 'react-router'
1. Rendering a will navigate to a new location.
2. The new location will override the current location in the history stack,
like server-side redirects (HTTP 3xx) do.

2. 🔨Modify 📁web ➡ 📃App.tsx

import { ApolloClient, InMemoryCache, ApolloProvider, HttpLink } from '@apollo/client';
import Users from './components/Users';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
import Landing from './components/Landing';
import {setContext} from 'apollo-link-context';
import Signup from './pages/Signup';
import Login from './pages/Login';
import IsAuthenticated from './components/IsAuthenticated';

const httpLink = new HttpLink({uri:'http://localhost:4000'})
const authlink = setContext( async( req, { headers } ) => {

  const token = localStorage.getItem('token')

  return {
    ...headers,
    headers:{
      Authorization: token ? `Bearer ${token}` : null
    }
  }
})

const link = authlink.concat(httpLink as any)

const client = new ApolloClient(
 {
  link: (link as any),
  cache: new InMemoryCache()}
)
function App() {
  return (
    <ApolloProvider client={client}>
      <Router>
        <Switch>
          <Route path="/landing">
            <Landing />
          </Route>
          <Route path="/signup">
            <Signup />
          </Route>
            <Route path="/login">
            <Login />
          </Route>
          <IsAuthenticated>
          🚗 <Route path="/users">
              	<Users />
             </Route>
          </IsAuthenticated>
        </Switch>
      </Router>
    </ApolloProvider>
  );
}

export default App;

📋 Memo(IsAuthenticated.tsx)

🚗 <Route path="/users"> <Users /> </Route>
1. IsAuthenticated.tsx's Props.children

➰ Prisma Profile Creation and Mutation

1. 🔨Modify 📁server ➡ 📁prisma ➡ 📃schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

// DataBadse로 집어넣을 정보들에 대한 Schema
model Post {
  id        Int     @id @default(autoincrement())
  title     String
  content   String?
  published Boolean @default(false)
  author    User?   @relation(fields: [authorId], references: [id])
  authorId  Int?
}

model User {
  id       Int      @id @default(autoincrement())
  email    String   @unique
  password String   @default("")
  name     String?
  Profile  Profile?
  tweets   Tweet[]
  Post     Post[]
}

model Profile {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  bio       String?
  location  String?
  website   String?
  avatar    String?
  userId    Int?     @unique
  User      User?    @relation(fields: [userId], references: [id])
}

model Tweet {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  content   String
  author    User?    @relation(fields: [authorID], references: [id])
  authorID  Int?
}
// npx prisma migrate dev --preview-feature
// Manually writing the data model and mapping it to the database with Prisma Migrate

2. 📝Create 📁server ➡ 📁types ➡ 📃Profile.ts

import { objectType } from 'nexus'

export const Profile = objectType({
  name: 'Profile',
  definition(t) {
    t.model.id()
    t.model.bio()
    t.model.location()
    t.model.website()
    t.model.avatar()
  },
})

3. 📝Create 📁server ➡ 📁types ➡ 📃Tweet.ts

import { objectType } from 'nexus'

export const Tweet = objectType({
  name: 'Tweet',
  definition(t) {
    t.model.id()
    t.model.content()
    t.model.author()
  },
})

4. 🔨Modify 📁server ➡ 📁types ➡ 📃Mutation.ts

import { compare, hash } from 'bcryptjs'
import { sign } from 'jsonwebtoken'
import { intArg, mutationType, nonNull, stringArg } from 'nexus'
import { APP_SECRET, getUserId } from '../utils'

export const Mutation = mutationType({
  definition(t) {
    t.field('signup', {
      type: 'AuthPayload',
      args: {
        name: stringArg(),
        email: nonNull(stringArg()),
        password: nonNull(stringArg()),
      },
      resolve: async (_parent, { name, email, password }, ctx) => {
        const hashedPassword = await hash(password, 10)
        const user = await ctx.prisma.user.create({
          data: {
            name,
            email,
            password: hashedPassword,
          },
        })
        return {
          token: sign({ userId: user.id }, APP_SECRET),
          user,
        }
      },
    })

    t.field('login', {
      type: 'AuthPayload',
      args: {
        email: nonNull(stringArg()),
        password: nonNull(stringArg()),
      },
      resolve: async (_parent, { email, password }, ctx) => {
        const user = await ctx.prisma.user.findUnique({
          where: {
            email,
          },
        })
        if (!user) {
          throw new Error(`No user found for email: ${email}`)
        }
        const passwordValid = await compare(password, user.password)
        if (!passwordValid) {
          throw new Error('Invalid password')
        }
        return {
          token: sign({ userId: user.id }, APP_SECRET),
          user,
        }
      },
    })
    t.field("createProfile", {
			type: "Profile",
			args: {
				bio: stringArg(),
				location: stringArg(),
				website: stringArg(),
				avatar: stringArg()
			},
			resolve: (parent, args, ctx) => {
				const userId = getUserId(ctx)
				if (!userId) throw new Error("Could not authenticate user.")
				return ctx.prisma.profile.create({
					data: {
						...args,
						User: { connect: { id: Number(userId) } }
					}
				})
			}
		})
      t.field("updateProfile", {
			type: "Profile",
      args: {
        id: intArg(),
				bio: stringArg(),
				location: stringArg(),
				website: stringArg(),
				avatar: stringArg()
			},
			resolve: (parent, {id, ...args}, ctx) => {
				const userId = getUserId(ctx)
				if (!userId) throw new Error("Could not authenticate user.")
				return ctx.prisma.profile.update({
					data: {
						...args,
          },
          where: {
            id: Number(id)
          }
				})
			}
		})
    // t.field('createDraft', {
    //   type: 'Post',
    //   args: {
    //     title: nonNull(stringArg()),
    //     content: stringArg(),
    //   },
    //   resolve: (parent, { title, content }, ctx) => {
    //     const userId = getUserId(ctx)
    //     if (!userId) throw new Error('Could not authenticate user.')
    //     return ctx.prisma.post.create({
    //       data: {
    //         title,
    //         content,
    //         published: false,
    //         author: { connect: { id: Number(userId) } },
    //       },
    //     })
    //   },
    // })

    // t.nullable.field('deletePost', {
    //   type: 'Post',
    //   args: { id: nonNull(intArg()) },
    //   resolve: (parent, { id }, ctx) => {
    //     return ctx.prisma.post.delete({
    //       where: {
    //         id,
    //       },
    //     })
    //   },
    // })
    // t.nullable.field('publish', {
    //   type: 'Post',
    //   args: {
    //     id: nonNull(intArg()),
    //   },
    //   resolve: (parent, { id }, ctx) => {
    //     return ctx.prisma.post.update({
    //       where: { id },
    //       data: { published: true },
    //     })
    //   },
    // })
  },
})

5. 🔨Modify 📁server ➡ 📁types ➡ 📃Query.ts

import { intArg, nullable, queryType, stringArg } from 'nexus'
import { getUserId } from '../utils'

export const Query = queryType({
definition(t) {
  t.nullable.field('me', {
    type: 'User',
    resolve: (parent, args, ctx) => {
      const userId = getUserId(ctx)
      return ctx.prisma.user.findUnique({
        where: {
          id: Number(userId),
        },
      })
    },
  })

  t.list.field('users', {
    type: 'User',
    resolve: (parent, args, ctx) => {
      return ctx.prisma.user.findMany()
    },
  })

  // t.list.field('filterPosts', {
  //   type: 'Post',
  //   args: {
  //     searchString: nullable(stringArg()),
  //   },
  //   resolve: (parent, { searchString }, ctx) => {
  //     return ctx.prisma.post.findMany({
  //       where: {
  //         OR: [
  //           {
  //             title: {
  //               contains: searchString || undefined,
  //             },
  //           },
  //           {
  //             content: {
  //               contains: searchString ?? undefined,
  //             },
  //           },
  //         ],
  //       },
  //     })
  //   },
  // })

  // t.nullable.field('post', {
  //   type: 'Post',
  //   args: { id: intArg() },
  //   resolve: (parent, { id }, ctx) => {
  //     return ctx.prisma.post.findUnique({
  //       where: {
  //         id: Number(id),
  //       },
  //     })
  //   },
  // })
},
})
profile
https://github.com/Coaspe

0개의 댓글