]> Untitled Git - lemmy-ui.git/blob - src/server/handlers/catch-all-handler.tsx
Fix search page breaking on initial load when logged in (#1781)
[lemmy-ui.git] / src / server / handlers / catch-all-handler.tsx
1 import { initializeSite, isAuthPath } from "@utils/app";
2 import { getHttpBaseInternal } from "@utils/env";
3 import { ErrorPageData } from "@utils/types";
4 import * as cookie from "cookie";
5 import fetch from "cross-fetch";
6 import type { Request, Response } from "express";
7 import { StaticRouter, matchPath } from "inferno-router";
8 import { renderToString } from "inferno-server";
9 import { GetSite, GetSiteResponse, LemmyHttp } from "lemmy-js-client";
10 import { App } from "../../shared/components/app/app";
11 import {
12   InitialFetchRequest,
13   IsoDataOptionalSite,
14   RouteData,
15 } from "../../shared/interfaces";
16 import { routes } from "../../shared/routes";
17 import {
18   FailedRequestState,
19   wrapClient,
20 } from "../../shared/services/HttpService";
21 import { createSsrHtml } from "../utils/create-ssr-html";
22 import { getErrorPageData } from "../utils/get-error-page-data";
23 import { setForwardedHeaders } from "../utils/set-forwarded-headers";
24
25 export default async (req: Request, res: Response) => {
26   try {
27     const activeRoute = routes.find(route => matchPath(req.path, route));
28
29     let auth = req.headers.cookie
30       ? cookie.parse(req.headers.cookie).jwt
31       : undefined;
32
33     const getSiteForm: GetSite = { auth };
34
35     const headers = setForwardedHeaders(req.headers);
36
37     const client = wrapClient(
38       new LemmyHttp(getHttpBaseInternal(), { fetchFunction: fetch, headers })
39     );
40
41     const { path, url, query } = req;
42
43     // Get site data first
44     // This bypasses errors, so that the client can hit the error on its own,
45     // in order to remove the jwt on the browser. Necessary for wrong jwts
46     let site: GetSiteResponse | undefined = undefined;
47     let routeData: RouteData = {};
48     let errorPageData: ErrorPageData | undefined = undefined;
49     let try_site = await client.getSite(getSiteForm);
50
51     if (try_site.state === "failed" && try_site.msg == "not_logged_in") {
52       console.error(
53         "Incorrect JWT token, skipping auth so frontend can remove jwt cookie"
54       );
55       getSiteForm.auth = undefined;
56       auth = undefined;
57       try_site = await client.getSite(getSiteForm);
58     }
59
60     if (!auth && isAuthPath(path)) {
61       return res.redirect("/login");
62     }
63
64     if (try_site.state === "success") {
65       site = try_site.data;
66       initializeSite(site);
67
68       if (path !== "/setup" && !site.site_view.local_site.site_setup) {
69         return res.redirect("/setup");
70       }
71
72       if (site && activeRoute?.fetchInitialData) {
73         const initialFetchReq: InitialFetchRequest = {
74           client,
75           auth,
76           path,
77           query,
78           site,
79         };
80
81         routeData = await activeRoute.fetchInitialData(initialFetchReq);
82       }
83
84       if (!activeRoute) {
85         res.status(404);
86       }
87     } else if (try_site.state === "failed") {
88       res.status(500);
89       errorPageData = getErrorPageData(new Error(try_site.msg), site);
90     }
91
92     const error = Object.values(routeData).find(
93       res => res.state === "failed" && res.msg !== "couldnt_find_object" // TODO: find a better way of handling errors
94     ) as FailedRequestState | undefined;
95
96     // Redirect to the 404 if there's an API error
97     if (error) {
98       console.error(error.msg);
99
100       if (error.msg === "instance_is_private") {
101         return res.redirect(`/signup`);
102       } else {
103         res.status(500);
104         errorPageData = getErrorPageData(new Error(error.msg), site);
105       }
106     }
107
108     const isoData: IsoDataOptionalSite = {
109       path,
110       site_res: site,
111       routeData,
112       errorPageData,
113     };
114
115     const wrapper = (
116       <StaticRouter location={url} context={isoData}>
117         <App />
118       </StaticRouter>
119     );
120
121     const root = renderToString(wrapper);
122
123     res.send(await createSsrHtml(root, isoData));
124   } catch (err) {
125     // If an error is caught here, the error page couldn't even be rendered
126     console.error(err);
127     res.statusCode = 500;
128
129     return res.send(
130       process.env.NODE_ENV === "development" ? err.message : "Server error"
131     );
132   }
133 };