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";
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";
19 export default async (req: Request, res: Response) => {
21 const activeRoute = routes.find(route => matchPath(req.path, route));
22 let auth: string | undefined = IsomorphicCookie.load("jwt", req);
24 const getSiteForm: GetSite = { auth };
26 const headers = setForwardedHeaders(req.headers);
27 const client = wrapClient(new LemmyHttp(getHttpBaseInternal(), headers));
29 const { path, url, query } = req;
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") {
40 "Incorrect JWT token, skipping auth so frontend can remove jwt cookie"
42 getSiteForm.auth = undefined;
44 try_site = await client.getSite(getSiteForm);
47 if (!auth && isAuthPath(path)) {
48 return res.redirect("/login");
51 if (try_site.state === "success") {
55 if (path !== "/setup" && !site.site_view.local_site.site_setup) {
56 return res.redirect("/setup");
60 const initialFetchReq: InitialFetchRequest = {
68 if (activeRoute?.fetchInitialData) {
70 ...(await Promise.all([
71 ...activeRoute.fetchInitialData(initialFetchReq),
76 } else if (try_site.state === "failed") {
77 errorPageData = getErrorPageData(new Error(try_site.msg), site);
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;
84 if (error === "instance_is_private") {
85 return res.redirect(`/signup`);
87 errorPageData = getErrorPageData(new Error(error), site);
91 const isoData: IsoDataOptionalSite = {
99 <StaticRouter location={url} context={isoData}>
104 const root = renderToString(wrapper);
106 res.send(await createSsrHtml(root, isoData));
108 // If an error is caught here, the error page couldn't even be rendered
110 res.statusCode = 500;
112 process.env.NODE_ENV === "development" ? err.message : "Server error"