Skip to content

Commit

Permalink
add authentication context + ssr (#57)
Browse files Browse the repository at this point in the history
* add authentication context + ssr

* fix lint
  • Loading branch information
mahmoudmoravej authored Feb 25, 2024
1 parent 5127590 commit e4fed31
Show file tree
Hide file tree
Showing 28 changed files with 341 additions and 261 deletions.
85 changes: 74 additions & 11 deletions app/@types/graphql/schema.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import { ApolloProvider } from "@apollo/client";
import { useAuthenticationContext } from "../authentication/authenticationContext";
import { useMemo } from "react";

export const AppApolloProvider = ({
export const ApolloClientProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const { user } = useAuthenticationContext();

const client = useMemo(() => {
console.log("Client is created!!!"); //TODO:
return getApolloClient(
sessionStorage.getItem("graphql_url"),
user?.jwt_token,
Expand Down
15 changes: 15 additions & 0 deletions app/contexts/apollo/apolloServerProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {
ApolloClient,
ApolloProvider,
NormalizedCacheObject,
} from "@apollo/client";

export const ApolloServerProvider = ({
client,
children,
}: {
client: ApolloClient<NormalizedCacheObject>;
children: React.ReactNode;
}) => {
return <ApolloProvider client={client}>{children}</ApolloProvider>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@ import { useState } from "react";
import { User } from "~/models/user";
import { AuthenticationContext } from "./authenticationContext";

export const AuthenticationProvider = ({
export const AuthenticationClientProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const [user, setUser] = useState<User | undefined>();
const [user, setUser] = useState<User>(window.__USER_STATE__);

return (
<AuthenticationContext.Provider value={{ user: user, setUser: setUser }}>
<AuthenticationContext.Provider
value={{
user: user,
setUser: setUser,
isAuthenticated: user != null,
}}
>
{children}
</AuthenticationContext.Provider>
);
Expand Down
22 changes: 22 additions & 0 deletions app/contexts/authentication/AuthenticationServerProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { User } from "~/models/user";
import { AuthenticationContext } from "./authenticationContext";

export const AuthenticationServerProvider = ({
user,
children,
}: {
user: User | null;
children: React.ReactNode;
}) => {
return (
<AuthenticationContext.Provider
value={{
user: user,
setUser: () => {},
isAuthenticated: user != null,
}}
>
{children}
</AuthenticationContext.Provider>
);
};
8 changes: 5 additions & 3 deletions app/contexts/authentication/authenticationContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import React from "react";
import { User } from "~/models/user";

export const AuthenticationContext = React.createContext<{
user?: User;
setUser: React.Dispatch<React.SetStateAction<User | undefined>>;
user: User | null;
setUser: React.Dispatch<React.SetStateAction<User>>;
isAuthenticated: boolean;
}>({
user: undefined,
user: null,
setUser: () => {},
isAuthenticated: false,
});

export function useAuthenticationContext() {
Expand Down
6 changes: 4 additions & 2 deletions app/contexts/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from "./authentication/authenticationContext";
export * from "./authentication/authenticationProvider";
export * from "./appApollo/appApolloProvider";
export * from "./authentication/AuthenticationClientProvider";
export * from "./authentication/AuthenticationServerProvider";
export * from "./apollo/apolloClientProvider";
export * from "./apollo/apolloServerProvider";
10 changes: 5 additions & 5 deletions app/entry.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ import { ThemeProvider } from "@material-tailwind/react";
import { RemixBrowser } from "@remix-run/react";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import { AppApolloProvider, AuthenticationProvider } from "./contexts";
import { ApolloClientProvider, AuthenticationClientProvider } from "./contexts";

startTransition(() => {
hydrateRoot(
document,
<StrictMode>
<AuthenticationProvider>
<AppApolloProvider>
<AuthenticationClientProvider>
<ApolloClientProvider>
<ThemeProvider>
<RemixBrowser />
</ThemeProvider>
</AppApolloProvider>
</AuthenticationProvider>
</ApolloClientProvider>
</AuthenticationClientProvider>
</StrictMode>,
);
});
33 changes: 25 additions & 8 deletions app/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import { RemixServer } from "@remix-run/react";
import isbot from "isbot";
import { renderToPipeableStream } from "react-dom/server";

import { ApolloProvider } from "@apollo/client";
import { ApolloClient, NormalizedCacheObject } from "@apollo/client";
import { getDataFromTree } from "@apollo/client/react/ssr";
import { authenticator } from "./services/auth.server";
import type { ReactElement } from "react";
import * as utils from "./utils";
import { AuthenticationServerProvider, ApolloServerProvider } from "./contexts";
import { User } from "./models/user";

const ABORT_DELAY = 5_000;
// process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; //TODO: remove this line. It is dangerous. We have it because there is an issue with the SSL certificate chain of render.com in production
Expand Down Expand Up @@ -152,9 +154,10 @@ async function wrapRemixServerWithApollo(
remixServer: ReactElement,
request: Request,
) {
const client = await buildApolloClient(request);
const user = await authenticator.isAuthenticated(request);
const client = await buildApolloClient(user);

const app = <ApolloProvider client={client}>{remixServer}</ApolloProvider>;
const app = getServerApp(user, client, remixServer);

await getDataFromTree(app);
const initialState = client.extract();
Expand All @@ -164,22 +167,36 @@ async function wrapRemixServerWithApollo(
{app}
<script
dangerouslySetInnerHTML={{
__html: `window.__APOLLO_STATE__=${JSON.stringify(
initialState,
).replace(/</g, "\\u003c")}`, // The replace call escapes the < character to prevent cross-site scripting attacks that are possible via the presence of </script> in a string literal
__html: `
window.__APOLLO_STATE__=${serializeState(initialState)};
window.__USER_STATE__=${serializeState(user)}`,
}}
/>
</>
);
return appWithData;
}

async function buildApolloClient(request: Request) {
let user = await authenticator.isAuthenticated(request);
function serializeState(state: any) {
return JSON.stringify(state).replace(/</g, "\\u003c"); // The replace call escapes the < character to prevent cross-site scripting attacks that are possible via the presence of </script> in a string literal
}

function buildApolloClient(user: User | null) {
return utils.getApolloClient(
process.env.GRAPHQL_SCHEMA_URL,
user?.jwt_token,
user?.organization_id.toString(),
);
}

function getServerApp(
user: User | null,
client: ApolloClient<NormalizedCacheObject>,
remixServer: ReactElement,
) {
return (
<AuthenticationServerProvider user={user}>
<ApolloServerProvider client={client}>{remixServer}</ApolloServerProvider>
</AuthenticationServerProvider>
);
}
3 changes: 2 additions & 1 deletion app/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ export type User = {
jwt_token: string;
name: string;
user_id: number;
individual_id?: number;
individual_id: number;
organization_id: number;
is_manager?: boolean;
isPersonal: boolean;
};
3 changes: 2 additions & 1 deletion app/routes/_auth.login.($id).tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export function SignIn() {

<div className="mt-6 flex justify-between gap-2">
<Typography variant="small" className="font-medium text-gray-900">
<a href="#">Forgot Password?</a>
<a href="/">Forgot Password?</a>
</Typography>
</div>
<div className="mt-8 space-y-4">
Expand Down Expand Up @@ -167,6 +167,7 @@ export function SignIn() {
<img
src="/images/pattern.png"
className="h-full w-full rounded-3xl object-cover"
alt=""
/>
</div>
</section>
Expand Down
10 changes: 1 addition & 9 deletions app/routes/_dashboard.cycles._index/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,13 @@ import {
CardFooter,
Spinner,
} from "@material-tailwind/react";
import { LoaderFunction, redirect } from "@remix-run/node";

import { Link } from "@remix-run/react";
import { authenticator } from "~/services/auth.server";
import { noNull } from "~/utils";
import { AssignMissedActivitiesButton } from "~/components/AssignMissedActivitiesButton";

const TABLE_HEAD = ["Title", "From", "To", ""];

export let loader: LoaderFunction = async ({ request }) => {
//we should completely change the following appraoch
let user = await authenticator.isAuthenticated(request);
if (!user) return redirect("/login");
else return { user };
};

export default function Cycles() {
let pageTitle = "Cycles";
let pageSubTitle = "";
Expand Down
95 changes: 49 additions & 46 deletions app/routes/_dashboard.individuals.$id.coach/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,20 @@ import { noNull } from "~/utils";

import { GenerateCycleSummaryButton } from "./components/GenerateCycleSummaryButton";
import { DefaultSkeleton } from "~/components/DefaultSkeleton";
import { useAuthenticationContext } from "~/contexts";

export default function IndividualCoach() {
const { id } = useParams();
if (id == null) throw new Error("id is null");
const { id: idParam } = useParams();
if (idParam == null) throw new Error("id is null");
const [open, setOpen] = useState(0);
const [isOnGneratingAdvice, setIsOnGneratingAdvice] = useState(false);
const [adviceList, setAdviceList] = useState<AdviceFragmentFragment[] | null>(
null,
);
const { user } = useAuthenticationContext();

let id = idParam == "me" ? user?.individual_id.toString() : idParam;
if (id == null) throw new Error("id is null");

const { data, loading, error } = useCoachIndividualQuery({
variables: { id: id },
Expand Down Expand Up @@ -127,21 +132,20 @@ export default function IndividualCoach() {
</Link>
</Tooltip>
</Typography>

<Typography color="gray" className="mb-8 font-normal">
{isOnGneratingAdvice ? (
<DefaultSkeleton />
) : cycle.advice?.activitySummary ? (
<pre
className="whitespace-pre-wrap"
style={{ fontFamily: "inherit" }}
>
{cycle.advice.activitySummary}
</pre>
) : (
"-"
)}
</Typography>
<pre
className="whitespace-pre-wrap"
style={{ fontFamily: "inherit" }}
>
<Typography color="gray" className="mb-8 font-normal">
{isOnGneratingAdvice ? (
<DefaultSkeleton />
) : cycle.advice?.activitySummary ? (
cycle.advice.activitySummary
) : (
"-"
)}
</Typography>
</pre>
</CardBody>
</Card>
<Card className="flex-1">
Expand All @@ -165,42 +169,41 @@ export default function IndividualCoach() {
</Tooltip>
</Typography>

<Typography color="gray" className="mb-8 font-normal">
{isOnGneratingAdvice ? (
<DefaultSkeleton />
) : cycle.advice?.visionSummary ? (
<pre
className="whitespace-pre-wrap"
style={{ fontFamily: "inherit" }}
>
{cycle.advice.visionSummary}
</pre>
) : (
"-"
)}
</Typography>
<pre
className="whitespace-pre-wrap"
style={{ fontFamily: "inherit" }}
>
<Typography color="gray" className="mb-8 font-normal">
{isOnGneratingAdvice ? (
<DefaultSkeleton />
) : cycle.advice?.visionSummary ? (
cycle.advice.visionSummary
) : (
"-"
)}
</Typography>
</pre>
</CardBody>
</Card>
<Card className="flex-1">
<CardBody>
<Typography variant="h6" color="gray" className="mb-4 uppercase">
Coaching Advice
</Typography>

<Typography color="gray" className="mb-8 font-normal">
{isOnGneratingAdvice ? (
<DefaultSkeleton />
) : cycle.advice?.result ? (
<pre
className="whitespace-pre-wrap"
style={{ fontFamily: "inherit" }}
>
{cycle.advice.result}
</pre>
) : (
"-"
)}
</Typography>
<pre
className="whitespace-pre-wrap"
style={{ fontFamily: "inherit" }}
>
<Typography color="gray" className="mb-8 font-normal">
{isOnGneratingAdvice ? (
<DefaultSkeleton />
) : cycle.advice?.result ? (
cycle.advice.result
) : (
"-"
)}
</Typography>
</pre>
</CardBody>
</Card>
</div>
Expand Down
Loading

0 comments on commit e4fed31

Please sign in to comment.