Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GP-9: deploy on Netlify #4

Merged
merged 3 commits into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/out_publish
/out_functions
6 changes: 4 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
"project": "./tsconfig.json"
},
"rules": {
"jsx-a11y/anchor-is-valid": "off",
"react/react-in-jsx-scope": "off",
"react/jsx-props-no-spreading": "off",
"jsx-a11y/anchor-is-valid": "off",
"react/prop-types": "off",
"react/jsx-sort-props": [
"error",
{
Expand All @@ -16,6 +17,7 @@
"noSortAlphabetically": false,
"reservedFirst": true
}
]
],
"no-console": ["warn", { "allow": ["error"] }]
}
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

# production
/build
/out_functions
/out_publish

# misc
.DS_Store
Expand Down
3 changes: 0 additions & 3 deletions constants/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ const endpoints = {
login: '/api/login',
signup: '/api/signup',
logout: '/api/logout',

getUser: '/api/getuser',

hackathons: '/api/hackathons',
},
};
Expand Down
6 changes: 6 additions & 0 deletions constants/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const errors = {
login: 'Не удалось войти',
signup: 'Не удалось зарегистрироваться',
};

export default errors;
17 changes: 6 additions & 11 deletions context/authContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, {
createContext,
ReactNode,
useContext,
useEffect,
useState,
} from 'react';

Expand All @@ -21,14 +20,13 @@ interface ISignUp {
}

interface ILoginContext {
user: User | null
user?: User | null
login: (email: string, password: string) => void
logout: () => void
signup: (formData: ISignUp) => void
}

export const AuthContext = createContext<ILoginContext>({
user: null,
const AuthContext = createContext<ILoginContext>({
login: () => {},
logout: () => {},
signup: () => {},
Expand All @@ -37,18 +35,14 @@ export const AuthContext = createContext<ILoginContext>({
type AuthContextCompProps = {
children: ReactNode | ReactNode[]
token?: string
user: User
user?: User | null
};

export function AuthProvider({ children, user: initialUser }: AuthContextCompProps) {
const [user, setUser] = useState<User | null>(initialUser);
const [user, setUser] = useState(initialUser);

const router = useRouter();

useEffect(() => {
setUser(initialUser);
}, [initialUser]);

const login = async (email: string, password: string) => {
try {
const { data } = await axios.post<LoginResponse>(endpoints.api.login, { email, password });
Expand All @@ -63,6 +57,7 @@ export function AuthProvider({ children, user: initialUser }: AuthContextCompPro

const logout = async () => {
await axios.get<LoginResponse>(endpoints.api.logout);

setUser(null);
await router.push(endpoints.pages.login);
};
Expand All @@ -77,7 +72,7 @@ export function AuthProvider({ children, user: initialUser }: AuthContextCompPro
return (
<AuthContext.Provider value={{
user, login, logout, signup,
} as ILoginContext}
}}
>
{children}
</AuthContext.Provider>
Expand Down
9 changes: 9 additions & 0 deletions netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[build]
command = "yarn build"
functions = "out_functions"
publish = "out_publish"

[dev]
functions = "out_functions"
publish = "out_publish"
framework = "#static"
5 changes: 5 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// eslint-disable-next-line import/no-extraneous-dependencies
const { IgnorePlugin } = require('webpack');
const withPrefresh = require('@prefresh/next');
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
Expand Down Expand Up @@ -28,6 +30,8 @@ module.exports = withBundleAnalyzer(withPrefresh({
config.externals.push(
/^(preact|preact-render-to-string|preact-context-provider)([\\/]|$)/,
);
} else {
config.plugins.push(new IgnorePlugin(/firebase/));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

т.к. взаимодействуем с firebase теперь внутри getInitialProps, нужно явно исключать из бандла (несмотря на проверку req и res), иначе на этапе сборке клиента получаем ошибку (firebase-admin не может работать на клиенте)

}

// Install webpack aliases:
Expand All @@ -49,4 +53,5 @@ module.exports = withBundleAnalyzer(withPrefresh({

return config;
},
target: 'serverless',
}));
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "growth-point",
"name": "growth-points",
"version": "0.1.0",
"engineStrict": true,
"engines": {
Expand All @@ -8,7 +8,9 @@
"scripts": {
"dev": "next dev",
"build": "next build",
"postbuild": "next-on-netlify",
"start": "next start",
"start-netlify": "netlify dev",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

это просто для локального тестирования

"lint": "eslint --ext .ts,.tsx,.js ./",
"analyzer": "cross-env ANALYZE=true yarn build"
},
Expand All @@ -21,6 +23,7 @@
"cross-env": "7.0.2",
"firebase": "7.21.0",
"firebase-admin": "9.2.0",
"nanoid": "3.1.12",
"next": "9.5.3",
"preact": "10.5.2",
"preact-render-to-string": "5.1.10",
Expand All @@ -42,6 +45,8 @@
"eslint-plugin-react": "7.20.3",
"eslint-plugin-react-hooks": "4.0.8",
"husky": "4.3.0",
"netlify-cli": "2.64.1",
"next-on-netlify": "2.4.0",
"typescript": "4.0.3"
},
"husky": {
Expand Down
68 changes: 31 additions & 37 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,53 @@
import React from 'react';
import App, { AppContext, AppProps } from 'next/app';
import 'bulma/css/bulma.css';

import App, { AppContext, AppProps, AppInitialProps } from 'next/app';
import { AuthProvider } from '../context/authContext';
import endpoints from '../constants/endpoints';
import { User } from '../types/user';
import getOriginFromRequest from '../utils/server/getOriginFromRequest';
import redirectLogin from '../utils/auth/redirect';

import 'bulma/css/bulma.css';

type MyProps = {
user: User
user?: User
};

function MyApp({
Component, pageProps, user,
}: AppProps & MyProps) {
return (
<AuthProvider user={user}>
<Component {...pageProps} />
</AuthProvider>
);
}

MyApp.getInitialProps = async (appContext: AppContext): Promise<any> => {
const appProps = await App.getInitialProps(appContext);
const MyApp = ({ Component, pageProps, user }: AppProps & MyProps) => (
<AuthProvider user={user}>
<Component {...pageProps} />
</AuthProvider>
);

MyApp.getInitialProps = async (appContext: AppContext): Promise<AppInitialProps & MyProps> => {
const appProps = await App.getInitialProps(appContext);
const { res, req } = appContext.ctx;

if (!req) return { ...appProps };
const url = getOriginFromRequest(req);

const { parse } = await import('cookie');
const { default: axios } = await import('axios');
if (!req || !res) {
return { ...appProps };
}

const { token } = parse(req.headers.cookie ?? '');
try {
const { parse } = await import('cookie');
const { token } = parse(req.headers.cookie ?? '');

let user = null;
if (token) {
const { default: getUser } = await import('../utils/firebase/getUser');
const userData = await getUser(token);

if (token) {
const { data: userRes, status } = await axios.get(url + endpoints.api.getUser, {
headers: {
token,
},
});
if (userData) {
return { ...appProps, user: userData };
}

if (status === 200) {
user = userRes;
redirectLogin(req, res);
} else {
redirectLogin(req, res);
}
} else {

return { ...appProps };
} catch (e) {
console.error(e);

redirectLogin(req, res);
}

return { ...appProps, user };
return { ...appProps };
}
};

export default MyApp;
25 changes: 0 additions & 25 deletions pages/api/getuser.ts

This file was deleted.

12 changes: 5 additions & 7 deletions pages/api/hackathons.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { NextApiRequest, NextApiResponse } from 'next';
import firebase from '../../utils/server/firebaseClient';
import { Hackathon } from '../../types/hackathon';
import getHackathons from '../../utils/firebase/getHackathons';

const getHackathons = async (_: NextApiRequest, res: NextApiResponse) => {
const hackathonsHandler = async (_: NextApiRequest, res: NextApiResponse) => {
try {
const hackathonsRef = firebase.database().ref('hackathons');
const snapshot = await hackathonsRef.once('value');
const hackathons: Hackathon[] = snapshot.val();
const hackathons = await getHackathons();

res.status(200).json({ hackathons });
} catch (e) {
res.status(500).json({ error: e.message });
}
};

export default getHackathons;
export default hackathonsHandler;
27 changes: 10 additions & 17 deletions pages/api/login.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { serialize } from 'cookie';
import firebaseClient from '../../utils/server/firebaseClient';
import login from '../../utils/firebase/login';
import endpoints from '../../constants/endpoints';

const login = async (req: NextApiRequest, res: NextApiResponse) => {
const handleLogin = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const { email, password } = req.body;
await firebaseClient.auth().setPersistence(firebaseClient.auth.Auth.Persistence.NONE);
const { user, token, expirationTime } = await login(req.body);

const { user } = await firebaseClient.auth().signInWithEmailAndPassword(email, password);
const tokenResult = await user?.getIdTokenResult();
res.setHeader('Set-Cookie', serialize('token', token, {
path: endpoints.pages.index,
httpOnly: true,
expires: new Date(expirationTime),
}));

await firebaseClient.auth().signOut();

res.setHeader(
'Set-Cookie',
serialize('token', tokenResult?.token ?? '', {
expires: new Date(tokenResult?.expirationTime ?? ''),
path: '/',
httpOnly: true,
}),
);
res.status(200).json({ user });
} catch (e) {
res.status(500).json({ error: e.message });
}
};

export default login;
export default handleLogin;
14 changes: 8 additions & 6 deletions pages/api/logout.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { serialize } from 'cookie';
import endpoints from '../../constants/endpoints';

const logout = async (req: NextApiRequest, res: NextApiResponse) => {
const handleLogout = async (_: NextApiRequest, res: NextApiResponse) => {
try {
res.setHeader(
'Set-Cookie',
serialize('token', '', { expires: new Date(), path: '/', httpOnly: true }),
);
res.setHeader('Set-Cookie', serialize('token', '', {
path: endpoints.pages.index,
httpOnly: true,
}));

res.status(200).json({ status: 'OK' });
} catch (e) {
res.status(500).json({ error: e.message });
}
};

export default logout;
export default handleLogout;
Loading