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