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";
15 } from "../../shared/interfaces";
16 import { routes } from "../../shared/routes";
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";
25 export default async (req: Request, res: Response) => {
27 const activeRoute = routes.find(route => matchPath(req.path, route));
29 let auth = req.headers.cookie
30 ? cookie.parse(req.headers.cookie).jwt
33 const getSiteForm: GetSite = { auth };
35 const headers = setForwardedHeaders(req.headers);
37 const client = wrapClient(
38 new LemmyHttp(getHttpBaseInternal(), { fetchFunction: fetch, headers })
41 const { path, url, query } = req;
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);
51 if (try_site.state === "failed" && try_site.msg == "not_logged_in") {
53 "Incorrect JWT token, skipping auth so frontend can remove jwt cookie"
55 getSiteForm.auth = undefined;
57 try_site = await client.getSite(getSiteForm);
60 if (!auth && isAuthPath(path)) {
61 return res.redirect("/login");
64 if (try_site.state === "success") {
68 if (path !== "/setup" && !site.site_view.local_site.site_setup) {
69 return res.redirect("/setup");
72 if (site && activeRoute?.fetchInitialData) {
73 const initialFetchReq: InitialFetchRequest = {
81 routeData = await activeRoute.fetchInitialData(initialFetchReq);
87 } else if (try_site.state === "failed") {
89 errorPageData = getErrorPageData(new Error(try_site.msg), site);
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;
96 // Redirect to the 404 if there's an API error
98 console.error(error.msg);
100 if (error.msg === "instance_is_private") {
101 return res.redirect(`/signup`);
104 errorPageData = getErrorPageData(new Error(error.msg), site);
108 const isoData: IsoDataOptionalSite = {
116 <StaticRouter location={url} context={isoData}>
117 <App user={site?.my_user} />
121 const root = renderToString(wrapper);
123 res.send(await createSsrHtml(root, isoData));
125 // If an error is caught here, the error page couldn't even be rendered
127 res.statusCode = 500;
130 process.env.NODE_ENV === "development" ? err.message : "Server error"