├── README.md
├── .eslintrc.json
├── next.config.js
├── public
├── favicon.ico
└── vercel.svg
├── postcss.config.js
├── reducers
├── index.js
└── auth.js
├── tailwind.config.js
├── pages
├── _app.js
├── index.js
├── api
│ └── account
│ │ ├── logout.js
│ │ ├── register.js
│ │ ├── user.js
│ │ ├── verify.js
│ │ ├── refresh.js
│ │ └── login.js
├── login.js
└── register.js
├── .gitignore
├── styles
└── globals.css
├── package.json
├── components
├── Layout.js
└── Navigation.js
├── actions
├── types.js
└── auth.js
└── store.js
/README.md:
--------------------------------------------------------------------------------
1 | # nextjs-account-auth-finish
2 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | reactStrictMode: true,
3 | }
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eien0618/nextjs-account-auth-finish/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import authReducer from './auth'
3 |
4 | export default combineReducers({
5 | auth: authReducer,
6 | })
7 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | mode: 'jit',
3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
4 | darkMode: false, // or 'media' or 'class'
5 | theme: {
6 | extend: {},
7 | },
8 | variants: {
9 | extend: {},
10 | },
11 | plugins: [],
12 | }
13 |
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import { Provider } from 'react-redux'
2 | import { useStore } from '../store'
3 | import Layout from '../components/Layout'
4 | import '../styles/globals.css'
5 |
6 | function MyApp({ Component, pageProps }) {
7 | const store = useStore(pageProps.initialReduxState)
8 |
9 | return (
10 |
11 |
12 |
13 |
14 |
15 | )
16 | }
17 |
18 | export default MyApp
19 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react'
2 | import { useSelector } from 'react-redux'
3 | import Head from 'next/head'
4 | import Link from 'next/link'
5 |
6 | const Index = () => {
7 | const isAuthenticated = useSelector((state) => state.auth.isAuthenticated)
8 | const user = useSelector((state) => state.auth.user)
9 |
10 | return (
11 | <>
12 |
13 | Instagram Clone
14 |
15 | >
16 | )
17 | }
18 |
19 | export default Index
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
36 | .env
37 |
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | .input-form {
6 | @apply w-full bg-gray-100 bg-opacity-50 rounded border border-gray-300 focus:border-yellow-500 focus:bg-transparent focus:ring-2 focus:ring-yellow-300 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out;
7 | }
8 |
9 | .button-yellow {
10 | @apply text-center inline-block text-white bg-yellow-500 py-2 px-4 focus:outline-none hover:bg-yellow-600 rounded;
11 | }
12 |
13 | .button-nav {
14 | @apply inline-block bg-white py-2 px-4 rounded font-medium text-gray-500 hover:bg-gray-50;
15 | }
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs-instagram-clone",
3 | "private": true,
4 | "scripts": {
5 | "dev": "next dev",
6 | "build": "next build",
7 | "start": "next start",
8 | "lint": "next lint"
9 | },
10 | "dependencies": {
11 | "cookie": "^0.4.1",
12 | "next": "^11.1.2",
13 | "react": "17.0.2",
14 | "react-dom": "17.0.2",
15 | "react-loader-spinner": "^4.0.0",
16 | "react-redux": "^7.2.6",
17 | "redux": "^4.1.2",
18 | "redux-devtools-extension": "^2.13.9",
19 | "redux-thunk": "^2.4.0"
20 | },
21 | "devDependencies": {
22 | "autoprefixer": "^10.4.0",
23 | "eslint": "7.32.0",
24 | "eslint-config-next": "12.0.3",
25 | "postcss": "^8.3.11",
26 | "tailwindcss": "^2.2.19"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/components/Layout.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useDispatch } from 'react-redux'
3 | import { refresh } from '../actions/auth'
4 | import Head from 'next/head'
5 | import Navigation from './Navigation'
6 |
7 | const Layout = (props) => {
8 | const dispatch = useDispatch()
9 |
10 | useEffect(() => {
11 | const fn = async () => {
12 | if (dispatch && dispatch !== null && dispatch !== undefined) {
13 | await dispatch(refresh())
14 | }
15 | }
16 | fn()
17 | }, [dispatch])
18 |
19 | return (
20 | <>
21 |
22 |
23 |
24 |
25 | {props.children}
26 | >
27 | )
28 | }
29 |
30 | export default Layout
31 |
--------------------------------------------------------------------------------
/pages/api/account/logout.js:
--------------------------------------------------------------------------------
1 | import cookie from 'cookie'
2 |
3 | export default async (req, res) => {
4 | if (req.method === 'POST') {
5 | res.setHeader('Set-Cookie', [
6 | cookie.serialize('access', '', {
7 | httpOnly: false,
8 | secure: true,
9 | sameSite: 'Lax',
10 | path: '/',
11 | expires: new Date(0),
12 | }),
13 | cookie.serialize('refresh', '', {
14 | httpOnly: false,
15 | secure: true,
16 | sameSite: 'Lax',
17 | path: '/',
18 | expires: new Date(0),
19 | }),
20 | ])
21 |
22 | return res.status(200).json({
23 | success: 'ログアウトに成功しました',
24 | })
25 | } else {
26 | res.setHeader('Allow', ['POST'])
27 | return res.status(405).json({
28 | error: `Method ${req.method} now allowed`,
29 | })
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/actions/types.js:
--------------------------------------------------------------------------------
1 | // ユーザー登録
2 | export const REGISTER_SUCCESS = 'REGISTER_SUCCESS'
3 | export const REGISTER_FAIL = 'REGISTER_FAIL'
4 |
5 | // ログイン
6 | export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'
7 | export const LOGIN_FAIL = 'LOGIN_FAIL'
8 |
9 | // ユーザー情報取得
10 | export const USER_SUCCESS = 'USER_SUCCESS'
11 | export const USER_FAIL = 'USER_FAIL'
12 |
13 | // リフレッシュトークン
14 | export const REFRESH_SUCCESS = 'REFRESH_SUCCESS'
15 | export const REFRESH_FAIL = 'REFRESH_FAIL'
16 |
17 | // 認証チェック
18 | export const AUTHENTICATED_SUCCESS = 'AUTHENTICATED_SUCCESS'
19 | export const AUTHENTICATED_FAIL = 'AUTHENTICATED_FAIL'
20 |
21 | // ログアウト
22 | export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'
23 | export const LOGOUT_FAIL = 'LOGOUT_FAIL'
24 |
25 | // 読み込み中
26 | export const SET_AUTH_LOADING = 'SET_AUTH_LOADING'
27 | export const REMOVE_AUTH_LOADING = 'REMOVE_AUTH_LOADING'
28 |
--------------------------------------------------------------------------------
/store.js:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { createStore, applyMiddleware } from 'redux'
3 | import { composeWithDevTools } from 'redux-devtools-extension'
4 | import thunkMiddleware from 'redux-thunk'
5 | import reducers from './reducers'
6 |
7 | let store
8 |
9 | function initStore(initialState) {
10 | return createStore(reducers, initialState, composeWithDevTools(applyMiddleware(thunkMiddleware)))
11 | }
12 |
13 | export const initializeStore = (preloadedState) => {
14 | let _store = store ?? initStore(preloadedState)
15 |
16 | if (preloadedState && store) {
17 | _store = initStore({
18 | ...store.getState(),
19 | ...preloadedState,
20 | })
21 | store = undefined
22 | }
23 |
24 | if (typeof window === 'undefined') return _store
25 | if (!store) store = _store
26 |
27 | return _store
28 | }
29 |
30 | export function useStore(initialState) {
31 | const store = useMemo(() => initializeStore(initialState), [initialState])
32 | return store
33 | }
34 |
--------------------------------------------------------------------------------
/pages/api/account/register.js:
--------------------------------------------------------------------------------
1 | export default async (req, res) => {
2 | if (req.method === 'POST') {
3 | const { name, email, password } = req.body
4 |
5 | const body = JSON.stringify({
6 | name,
7 | email,
8 | password,
9 | })
10 |
11 | try {
12 | const apiRes = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/auth/register/`, {
13 | method: 'POST',
14 | headers: {
15 | 'Content-Type': 'application/json',
16 | },
17 | body: body,
18 | })
19 |
20 | if (apiRes.status === 201) {
21 | return res.status(200).json({
22 | success: 'アカウント登録に成功しました',
23 | })
24 | } else {
25 | return res.status(apiRes.status).json({
26 | error: 'アカウント登録に失敗しました',
27 | })
28 | }
29 | } catch (err) {
30 | return res.status(500).json({
31 | error: 'アカウント登録に失敗しました',
32 | })
33 | }
34 | } else {
35 | res.setHeader('Allow', ['POST'])
36 | return res.status(405).json({ error: `Method ${req.method} not allowed` })
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pages/api/account/user.js:
--------------------------------------------------------------------------------
1 | import cookie from 'cookie'
2 |
3 | export default async (req, res) => {
4 | if (req.method === 'GET') {
5 | const cookies = cookie.parse(req.headers.cookie ?? '')
6 | const access = cookies.access ?? false
7 |
8 | if (access === false) {
9 | return res.status(401).json({
10 | error: 'アクセストークンがありません',
11 | })
12 | }
13 |
14 | try {
15 | const apiRes = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/auth/user/`, {
16 | method: 'GET',
17 | headers: {
18 | Authorization: `Bearer ${access}`,
19 | },
20 | })
21 | const data = await apiRes.json()
22 |
23 | if (apiRes.status === 200) {
24 | return res.status(200).json({
25 | user: data.user,
26 | })
27 | } else {
28 | return res.status(apiRes.status).json({
29 | error: 'ユーザー情報取得に失敗しました',
30 | })
31 | }
32 | } catch (err) {
33 | return res.status(500).json({
34 | error: 'ユーザー情報取得に失敗しました',
35 | })
36 | }
37 | } else {
38 | res.setHeader('Allow', ['GET'])
39 | return res.status(405).json({
40 | error: `Method ${req.method} not allowed`,
41 | })
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/pages/api/account/verify.js:
--------------------------------------------------------------------------------
1 | import cookie from 'cookie'
2 |
3 | export default async (req, res) => {
4 | if (req.method === 'GET') {
5 | const cookies = cookie.parse(req.headers.cookie ?? '')
6 | const access = cookies.access ?? false
7 |
8 | if (access === false) {
9 | return res.status(403).json({
10 | error: 'アクセストークンがありません',
11 | })
12 | }
13 |
14 | const body = JSON.stringify({
15 | token: access,
16 | })
17 |
18 | try {
19 | const apiRes = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/verify/`, {
20 | method: 'POST',
21 | headers: {
22 | 'Content-Type': 'application/json',
23 | },
24 | body: body,
25 | })
26 |
27 | if (apiRes.status === 200) {
28 | return res.status(200).json({
29 | success: '認証に成功しました',
30 | })
31 | } else {
32 | return res.status(apiRes.status).json({
33 | error: '認証に失敗しました',
34 | })
35 | }
36 | } catch (err) {
37 | return res.status(500).json({
38 | error: '認証に失敗しました',
39 | })
40 | }
41 | } else {
42 | res.setHeader('Allow', ['GET'])
43 | return res.status(405).json({ error: `Method ${req.method} not allowed` })
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/pages/api/account/refresh.js:
--------------------------------------------------------------------------------
1 | import cookie from 'cookie'
2 |
3 | export default async (req, res) => {
4 | if (req.method === 'GET') {
5 | const cookies = cookie.parse(req.headers.cookie ?? '')
6 | const refresh = cookies.refresh ?? false
7 |
8 | if (refresh === false) {
9 | return res.status(401).json({
10 | error: 'リフレッシュトークンがありません',
11 | })
12 | }
13 |
14 | const body = JSON.stringify({
15 | refresh,
16 | })
17 |
18 | try {
19 | const apiRes = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/refresh/`, {
20 | method: 'POST',
21 | headers: {
22 | 'Content-Type': 'application/json',
23 | },
24 | body: body,
25 | })
26 |
27 | const data = await apiRes.json()
28 |
29 | if (apiRes.status === 200) {
30 | res.setHeader('Set-Cookie', [
31 | cookie.serialize('access', data.access, {
32 | httpOnly: false,
33 | secure: true,
34 | sameSite: 'Lax',
35 | path: '/',
36 | maxAge: 60 * 60 * 24, // 1日
37 | }),
38 | ])
39 |
40 | return res.status(200).json({
41 | success: 'リフレッシュトークン取得に成功しました',
42 | })
43 | } else {
44 | return res.status(apiRes.status).json({
45 | error: 'リフレッシュトークン取得に失敗しました',
46 | })
47 | }
48 | } catch (err) {
49 | return res.status(500).json({
50 | error: 'リフレッシュトークン取得に失敗しました',
51 | })
52 | }
53 | } else {
54 | res.setHeader('Allow', ['GET'])
55 | return res.status(405).json({ error: `Method ${req.method} not allowed` })
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/components/Navigation.js:
--------------------------------------------------------------------------------
1 | import Link from 'next/link'
2 | import { useSelector, useDispatch } from 'react-redux'
3 | import { logout } from '../actions/auth'
4 |
5 | const Navigation = () => {
6 | const dispatch = useDispatch()
7 | const isAuthenticated = useSelector((state) => state.auth.isAuthenticated)
8 |
9 | const logoutHandler = async () => {
10 | if (dispatch && dispatch !== null && dispatch !== undefined) {
11 | await dispatch(logout())
12 | }
13 | }
14 |
15 | return (
16 | <>
17 |
18 |
19 |
20 |
27 |
28 | {isAuthenticated ? (
29 |
30 | ログアウト
31 |
32 | ) : (
33 |
41 | )}
42 |
43 |
44 |
45 |
46 | >
47 | )
48 | }
49 |
50 | export default Navigation
51 |
--------------------------------------------------------------------------------
/pages/api/account/login.js:
--------------------------------------------------------------------------------
1 | import cookie from 'cookie'
2 |
3 | export default async (req, res) => {
4 | if (req.method === 'POST') {
5 | const { email, password } = req.body
6 |
7 | const body = JSON.stringify({
8 | email,
9 | password,
10 | })
11 |
12 | try {
13 | const apiRes = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/login/`, {
14 | method: 'POST',
15 | headers: {
16 | 'Content-Type': 'application/json',
17 | },
18 | body: body,
19 | })
20 |
21 | const data = await apiRes.json()
22 |
23 | if (apiRes.status === 200) {
24 | res.setHeader('Set-Cookie', [
25 | cookie.serialize('access', data.access, {
26 | httpOnly: false,
27 | secure: true,
28 | sameSite: 'Lax',
29 | path: '/',
30 | maxAge: 60 * 60, // 1時間
31 | }),
32 | cookie.serialize('refresh', data.refresh, {
33 | httpOnly: false,
34 | secure: true,
35 | sameSite: 'Lax',
36 | path: '/',
37 | maxAge: 60 * 60 * 24 * 3, // 3日
38 | }),
39 | ])
40 |
41 | return res.status(200).json({
42 | success: 'ログインに成功しました',
43 | })
44 | } else {
45 | return res.status(apiRes.status).json({
46 | error: 'ログインに失敗しました',
47 | })
48 | }
49 | } catch (err) {
50 | return res.status(500).json({
51 | error: 'ログインに失敗しました',
52 | })
53 | }
54 | } else {
55 | res.setHeader('Allow', ['POST'])
56 | return res.status(405).json({ error: `Method ${req.method} now allowed` })
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/pages/login.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import { useSelector, useDispatch } from 'react-redux'
3 | import { useRouter } from 'next/router'
4 | import { login } from '../actions/auth'
5 | import Loader from 'react-loader-spinner'
6 | import Head from 'next/head'
7 |
8 | const Login = () => {
9 | const dispatch = useDispatch()
10 | const router = useRouter()
11 | const isAuthenticated = useSelector((state) => state.auth.isAuthenticated)
12 | const loading = useSelector((state) => state.auth.loading)
13 |
14 | const [formData, setFormData] = useState({
15 | email: '',
16 | password: '',
17 | })
18 |
19 | const { email, password } = formData
20 |
21 | const onChange = (e) => {
22 | setFormData({ ...formData, [e.target.name]: e.target.value })
23 | }
24 |
25 | const onSubmit = async (e) => {
26 | e.preventDefault()
27 |
28 | if (dispatch && dispatch !== null && dispatch !== undefined) {
29 | await dispatch(login(email, password))
30 | }
31 | }
32 |
33 | if (typeof window !== 'undefined' && isAuthenticated) {
34 | router.push('/')
35 | }
36 |
37 | return (
38 | <>
39 |
40 | Instagram Clone | ログイン
41 |
42 |
43 | ログイン
44 |
85 | >
86 | )
87 | }
88 |
89 | export default Login
90 |
--------------------------------------------------------------------------------
/reducers/auth.js:
--------------------------------------------------------------------------------
1 | import {
2 | // ユーザー登録
3 | REGISTER_SUCCESS,
4 | REGISTER_FAIL,
5 |
6 | // ログイン
7 | LOGIN_SUCCESS,
8 | LOGIN_FAIL,
9 |
10 | // ユーザー情報取得
11 | USER_SUCCESS,
12 | USER_FAIL,
13 |
14 | // リフレッシュトークン
15 | REFRESH_SUCCESS,
16 | REFRESH_FAIL,
17 |
18 | // 認証チェック
19 | AUTHENTICATED_SUCCESS,
20 | AUTHENTICATED_FAIL,
21 |
22 | // ログアウト
23 | LOGOUT_SUCCESS,
24 | LOGOUT_FAIL,
25 |
26 | // 読み込み中
27 | SET_AUTH_LOADING,
28 | REMOVE_AUTH_LOADING,
29 | } from '../actions/types'
30 |
31 | const initialState = {
32 | user: null,
33 | isAuthenticated: null,
34 | loading: false,
35 | }
36 |
37 | const authReducer = (state = initialState, action) => {
38 | const { type, payload } = action
39 |
40 | switch (type) {
41 | // ユーザー登録
42 | case REGISTER_SUCCESS:
43 | return {
44 | ...state,
45 | }
46 | case REGISTER_FAIL:
47 | return {
48 | ...state,
49 | }
50 |
51 | // ログイン
52 | case LOGIN_SUCCESS:
53 | return {
54 | ...state,
55 | isAuthenticated: true,
56 | }
57 | case LOGIN_FAIL:
58 | return {
59 | ...state,
60 | isAuthenticated: false,
61 | }
62 |
63 | // ユーザー情報取得
64 | case USER_SUCCESS:
65 | return {
66 | ...state,
67 | user: payload.user,
68 | }
69 | case USER_FAIL:
70 | return {
71 | ...state,
72 | user: null,
73 | }
74 |
75 | // リフレッシュトークン
76 | case REFRESH_SUCCESS:
77 | return {
78 | ...state,
79 | }
80 | case REFRESH_FAIL:
81 | return {
82 | ...state,
83 | isAuthenticated: false,
84 | user: null,
85 | }
86 |
87 | // 認証チェック
88 | case AUTHENTICATED_SUCCESS:
89 | return {
90 | ...state,
91 | isAuthenticated: true,
92 | }
93 | case AUTHENTICATED_FAIL:
94 | return {
95 | ...state,
96 | isAuthenticated: false,
97 | user: null,
98 | }
99 |
100 | // ログアウト
101 | case LOGOUT_SUCCESS:
102 | return {
103 | ...state,
104 | isAuthenticated: false,
105 | user: null,
106 | }
107 | case LOGOUT_FAIL:
108 | return {
109 | ...state,
110 | }
111 |
112 | // 読み込み中
113 | case SET_AUTH_LOADING:
114 | return {
115 | ...state,
116 | loading: true,
117 | }
118 | case REMOVE_AUTH_LOADING:
119 | return {
120 | ...state,
121 | loading: false,
122 | }
123 | default:
124 | return state
125 | }
126 | }
127 |
128 | export default authReducer
129 |
--------------------------------------------------------------------------------
/pages/register.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import { useSelector, useDispatch } from 'react-redux'
3 | import { useRouter } from 'next/router'
4 | import { register } from '../actions/auth'
5 | import Loader from 'react-loader-spinner'
6 | import Head from 'next/head'
7 |
8 | const Register = () => {
9 | const dispatch = useDispatch()
10 | const router = useRouter()
11 | const isAuthenticated = useSelector((state) => state.auth.isAuthenticated)
12 | const loading = useSelector((state) => state.auth.loading)
13 |
14 | const [formData, setFormData] = useState({
15 | name: '',
16 | email: '',
17 | password: '',
18 | })
19 |
20 | const { name, email, password } = formData
21 |
22 | const onChange = (e) => {
23 | setFormData({ ...formData, [e.target.name]: e.target.value })
24 | }
25 |
26 | const onSubmit = async (e) => {
27 | e.preventDefault()
28 |
29 | if (dispatch && dispatch !== null && dispatch !== undefined) {
30 | await dispatch(register(name, email, password))
31 | }
32 | }
33 |
34 | if (typeof window !== 'undefined' && isAuthenticated) {
35 | router.push('/')
36 | }
37 |
38 | return (
39 | <>
40 |
41 | Instagram Clone | アカウント登録
42 |
43 |
44 | アカウント登録
45 |
100 | >
101 | )
102 | }
103 |
104 | export default Register
105 |
--------------------------------------------------------------------------------
/actions/auth.js:
--------------------------------------------------------------------------------
1 | import {
2 | // ユーザー登録
3 | REGISTER_SUCCESS,
4 | REGISTER_FAIL,
5 |
6 | // ログイン
7 | LOGIN_SUCCESS,
8 | LOGIN_FAIL,
9 |
10 | // ユーザー情報取得
11 | USER_SUCCESS,
12 | USER_FAIL,
13 |
14 | // リフレッシュトークン
15 | REFRESH_SUCCESS,
16 | REFRESH_FAIL,
17 |
18 | // 認証チェック
19 | AUTHENTICATED_SUCCESS,
20 | AUTHENTICATED_FAIL,
21 |
22 | // ログアウト
23 | LOGOUT_SUCCESS,
24 | LOGOUT_FAIL,
25 |
26 | // 読み込み中
27 | SET_AUTH_LOADING,
28 | REMOVE_AUTH_LOADING,
29 | } from './types'
30 |
31 | // ユーザー登録
32 | export const register = (name, email, password) => async (dispatch) => {
33 | dispatch({
34 | type: SET_AUTH_LOADING,
35 | })
36 |
37 | const body = JSON.stringify({
38 | name,
39 | email,
40 | password,
41 | })
42 |
43 | try {
44 | const res = await fetch('/api/account/register', {
45 | method: 'POST',
46 | headers: {
47 | 'Content-Type': 'application/json',
48 | },
49 | body: body,
50 | })
51 |
52 | if (res.status === 200) {
53 | dispatch({
54 | type: REGISTER_SUCCESS,
55 | })
56 | } else {
57 | dispatch({
58 | type: REGISTER_FAIL,
59 | })
60 | }
61 | } catch (err) {
62 | dispatch({
63 | type: REGISTER_FAIL,
64 | })
65 | }
66 |
67 | dispatch({
68 | type: REMOVE_AUTH_LOADING,
69 | })
70 | }
71 |
72 | // ログイン
73 | export const login = (email, password) => async (dispatch) => {
74 | dispatch({
75 | type: SET_AUTH_LOADING,
76 | })
77 |
78 | const body = JSON.stringify({
79 | email,
80 | password,
81 | })
82 |
83 | try {
84 | const res = await fetch('/api/account/login', {
85 | method: 'POST',
86 | headers: {
87 | 'Content-Type': 'application/json',
88 | },
89 | body: body,
90 | })
91 |
92 | if (res.status === 200) {
93 | dispatch({
94 | type: LOGIN_SUCCESS,
95 | })
96 | dispatch(user())
97 | } else {
98 | dispatch({
99 | type: LOGIN_FAIL,
100 | })
101 | }
102 | } catch (err) {
103 | dispatch({
104 | type: LOGIN_FAIL,
105 | })
106 | }
107 |
108 | dispatch({
109 | type: REMOVE_AUTH_LOADING,
110 | })
111 | }
112 |
113 | // ユーザー情報取得
114 | export const user = () => async (dispatch) => {
115 | dispatch({
116 | type: SET_AUTH_LOADING,
117 | })
118 |
119 | try {
120 | const res = await fetch('/api/account/user', {
121 | method: 'GET',
122 | })
123 |
124 | const data = await res.json()
125 |
126 | if (res.status === 200) {
127 | dispatch({
128 | type: USER_SUCCESS,
129 | payload: data,
130 | })
131 | } else {
132 | dispatch({
133 | type: USER_FAIL,
134 | })
135 | }
136 | } catch (err) {
137 | dispatch({
138 | type: USER_FAIL,
139 | })
140 | }
141 |
142 | dispatch({
143 | type: REMOVE_AUTH_LOADING,
144 | })
145 | }
146 |
147 | // リフレッシュトークン
148 | export const refresh = () => async (dispatch) => {
149 | dispatch({
150 | type: SET_AUTH_LOADING,
151 | })
152 |
153 | try {
154 | const res = await fetch('/api/account/refresh', {
155 | method: 'GET',
156 | })
157 |
158 | if (res.status === 200) {
159 | dispatch({
160 | type: REFRESH_SUCCESS,
161 | })
162 | dispatch(verify())
163 | } else {
164 | dispatch({
165 | type: REFRESH_FAIL,
166 | })
167 | }
168 | } catch (err) {
169 | dispatch({
170 | type: REFRESH_FAIL,
171 | })
172 | }
173 |
174 | dispatch({
175 | type: REMOVE_AUTH_LOADING,
176 | })
177 | }
178 |
179 | // 認証チェック
180 | export const verify = () => async (dispatch) => {
181 | dispatch({
182 | type: SET_AUTH_LOADING,
183 | })
184 |
185 | try {
186 | const res = await fetch('/api/account/verify', {
187 | method: 'GET',
188 | })
189 |
190 | if (res.status === 200) {
191 | dispatch({
192 | type: AUTHENTICATED_SUCCESS,
193 | })
194 | dispatch(user())
195 | } else {
196 | dispatch({
197 | type: AUTHENTICATED_FAIL,
198 | })
199 | }
200 | } catch (err) {
201 | dispatch({
202 | type: AUTHENTICATED_FAIL,
203 | })
204 | }
205 |
206 | dispatch({
207 | type: REMOVE_AUTH_LOADING,
208 | })
209 | }
210 |
211 | // ログアウト
212 | export const logout = () => async (dispatch) => {
213 | dispatch({
214 | type: SET_AUTH_LOADING,
215 | })
216 |
217 | try {
218 | const res = await fetch('/api/account/logout', {
219 | method: 'POST',
220 | })
221 |
222 | if (res.status === 200) {
223 | dispatch({
224 | type: LOGOUT_SUCCESS,
225 | })
226 | } else {
227 | dispatch({
228 | type: LOGOUT_FAIL,
229 | })
230 | }
231 | } catch (err) {
232 | dispatch({
233 | type: LOGOUT_FAIL,
234 | })
235 | }
236 |
237 | dispatch({
238 | type: REMOVE_AUTH_LOADING,
239 | })
240 | }
241 |
--------------------------------------------------------------------------------