Skip to content
This repository has been archived by the owner on Jan 17, 2023. It is now read-only.

Commit

Permalink
Extract rest calls pre render on index/dashboard, add error handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonraimondi committed Aug 6, 2019
1 parent 609842c commit bb4b522
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 44 deletions.
37 changes: 13 additions & 24 deletions web/components/private_route.tsx
Expand Up @@ -8,43 +8,32 @@ export type AuthProps = {
auth: AuthToken
}

type AuthState = {
auth: AuthToken
}

export function privateRoute(WrappedComponent: any) {
return class extends Component<AuthProps, AuthState> {
state = {
auth: new AuthToken(this.props.auth.token),
};

return class extends Component<AuthProps> {
static async getInitialProps(ctx: any) {
const token = ServerCookie(ctx)[COOKIES.authToken];
const auth = new AuthToken(token);
const initialProps = { auth };
if (auth.isExpired) {
ctx.res.writeHead(302, {
Location: "/login?redirected=true",
});
ctx.res.end();
}
if (WrappedComponent.getInitialProps) return WrappedComponent.getInitialProps(initialProps);
return initialProps;
}

componentDidMount(): void {
if (this.props.auth.decodedToken.exp === 0 || this.state.auth.isExpired) {
Router.push("/login");
}
}

componentDidUpdate(): void {
if (this.props.auth.decodedToken.exp === 0 || this.state.auth.isExpired) {
Router.push("/login");
}
}

render() {
get auth() {
// the server pass to the client serializes the token
// so we have to reinitialize the authToken class
//
// @see https://github.com/zeit/next.js/issues/3536
// const auth =;
return <WrappedComponent auth={this.state.auth} {...this.props} />;
return new AuthToken(this.props.auth.token);
}

render() {
return <WrappedComponent auth={this.auth} {...this.props} />;
}
};
}
41 changes: 30 additions & 11 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 32 additions & 3 deletions web/pages/dashboard.tsx
Expand Up @@ -3,19 +3,48 @@ import { AuthProps, privateRoute } from "../components/private_route";
import Cookie from "js-cookie";
import Router from "next/router";
import { COOKIES } from "../services/login_service";
import { Links } from "../components/links";
import { get } from "../services/rest_service";
import { AuthToken } from "../services/auth_token";

function Page(props: AuthProps) {
type Props = AuthProps & {
message: string
}

function Page(props: Props) {
const logout = async () => {
Cookie.remove(COOKIES.authToken);
alert("logout");
await Router.push("/login");
};

console.log(props.auth);

return <>
<p>Hello Dashboard</p>
<p>{JSON.stringify(props.auth)}</p>
<Links />
<p>{props.message}</p>
{/*<div>{props.auth.authorizationString}</div>*/}
{props.auth.isAuthenticated ? "YES" : "NO"}
<button onClick={logout}>Logout</button>
</>
}

Page.getInitialProps = async ({ auth }: AuthProps): Promise<Props> => {
const res: any = await get("/api/restricted", {
headers: {
Authorization: auth.authorizationString
}
});

let message = "Something unexpected happened!";

if (res.error) {
message = res.error;
} else if (res.data && res.data.message) {
message = res.data.message
}

return { message, auth, };
};

export default privateRoute(Page);
24 changes: 23 additions & 1 deletion web/pages/index.tsx
@@ -1,10 +1,32 @@
import React from "react";
import { Links } from "../components/links";
import { get } from "../services/rest_service";

function Page() {
type Props = {
message: string;
}

function Page({message}: Props) {
return <>
<Links/>
<p>The following is a result of a server side api call pre-render. If you right click and view source, the response from the API call will be visible in the source. This is different than say... Inspect Element, which shows the client side rendered content.</p>
<p>This means that search engines can scrape this page, and immediately see the content, without trusting that the search engines can render SPA's.</p>
<h2><small style={{color: "grey"}}>API Call:</small> {message}</h2>
</>;
}

Page.getInitialProps = async () => {
const res: any = await get("/api/unrestricted");

let message = "Something unexpected happened!";

if (res.error) {
message = res.error;
} else if (res.data && res.data.message) {
message = res.data.message
}

return { message };
};

export default Page;
2 changes: 1 addition & 1 deletion web/pages/login.tsx
Expand Up @@ -9,7 +9,7 @@ export type LoginInputs = {

function Page() {
// these values are hardcoded since our main.go api only accepts this auth combo
const initialValues: LoginInputs = { email: "rickety_cricket@example.com", password: "shhh!", };
const initialValues: LoginInputs = { email: "", password: "", };

const [inputs, setInputs] = useState(initialValues);
const [error, setError] = useState("");
Expand Down
6 changes: 2 additions & 4 deletions web/services/login_service.ts
Expand Up @@ -3,17 +3,15 @@ import Cookie from "js-cookie";
import Router from "next/router";
import { LoginInputs } from "../pages/login";
import { catchAxiosError } from "./error";
import { post } from "./rest_service";

export const COOKIES = {
authToken: "myApp.authToken"
};

export async function login(inputs: LoginInputs): Promise<string | void> {
const data = new URLSearchParams(inputs);
const config: AxiosRequestConfig = {
baseURL: "http://localhost:1323",
};
const res: any = await axios.post("/api/login", data, config).catch(catchAxiosError);
const res: any = await post("/api/login", data).catch(catchAxiosError);
if (res.error) {
return res.error;
} else if (!res.data || !res.data.token) {
Expand Down
19 changes: 19 additions & 0 deletions web/services/rest_service.ts
@@ -0,0 +1,19 @@
import axios, { AxiosRequestConfig } from "axios";
import { catchAxiosError } from "./error";


const baseConfig: AxiosRequestConfig = {
baseURL: "http://localhost:1323",
};

export const post = (url: string, data: URLSearchParams) => {
return axios.post(url, data, baseConfig).catch(catchAxiosError);
};

export const get = async (url: string, config: AxiosRequestConfig = {}) => {
const axiosConfig = {
...baseConfig,
...config,
};
return await axios.get(url, axiosConfig).catch(catchAxiosError)
};

0 comments on commit bb4b522

Please sign in to comment.