]> Untitled Git - lemmy-ui.git/blob - src/server/handlers/catch-all-handler.tsx
735c052c3e128f1643a75e4f85af5d3840659eee
[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     let auth = req.cookies ? cookie.parse(req.cookies).jwt : undefined;
29
30     const getSiteForm: GetSite = { auth };
31
32     const headers = setForwardedHeaders(req.headers);
33     const client = wrapClient(
34       new LemmyHttp(getHttpBaseInternal(), { fetchFunction: fetch, headers })
35     );
36
37     const { path, url, query } = req;
38
39     // Get site data first
40     // This bypasses errors, so that the client can hit the error on its own,
41     // in order to remove the jwt on the browser. Necessary for wrong jwts
42     let site: GetSiteResponse | undefined = undefined;
43     let routeData: RouteData = {};
44     let errorPageData: ErrorPageData | undefined = undefined;
45     let try_site = await client.getSite(getSiteForm);
46     if (try_site.state === "failed" && try_site.msg == "not_logged_in") {
47       console.error(
48         "Incorrect JWT token, skipping auth so frontend can remove jwt cookie"
49       );
50       getSiteForm.auth = undefined;
51       auth = undefined;
52       try_site = await client.getSite(getSiteForm);
53     }
54
55     if (!auth && isAuthPath(path)) {
56       return res.redirect("/login");
57     }
58
59     if (try_site.state === "success") {
60       site = try_site.data;
61       initializeSite(site);
62
63       if (path !== "/setup" && !site.site_view.local_site.site_setup) {
64         return res.redirect("/setup");
65       }
66
67       if (site && activeRoute?.fetchInitialData) {
68         const initialFetchReq: InitialFetchRequest = {
69           client,
70           auth,
71           path,
72           query,
73           site,
74         };
75
76         routeData = await activeRoute.fetchInitialData(initialFetchReq);
77       }
78
79       if (!activeRoute) {
80         res.status(404);
81       }
82     } else if (try_site.state === "failed") {
83       res.status(500);
84       errorPageData = getErrorPageData(new Error(try_site.msg), site);
85     }
86
87     const error = Object.values(routeData).find(
88       res => res.state === "failed"
89     ) as FailedRequestState | undefined;
90
91     // Redirect to the 404 if there's an API error
92     if (error) {
93       console.error(error.msg);
94       if (error.msg === "instance_is_private") {
95         return res.redirect(`/signup`);
96       } else {
97         res.status(500);
98         errorPageData = getErrorPageData(new Error(error.msg), site);
99       }
100     }
101
102     const isoData: IsoDataOptionalSite = {
103       path,
104       site_res: site,
105       routeData,
106       errorPageData,
107     };
108
109     const wrapper = (
110       <StaticRouter location={url} context={isoData}>
111         <App />
112       </StaticRouter>
113     );
114
115     const root = renderToString(wrapper);
116
117     res.send(await createSsrHtml(root, isoData));
118   } catch (err) {
119     // If an error is caught here, the error page couldn't even be rendered
120     console.error(err);
121     res.statusCode = 500;
122     return res.send(
123       process.env.NODE_ENV === "development" ? err.message : "Server error"
124     );
125   }
126 };