import serialize from "serialize-javascript"; import express from "express"; import { StaticRouter } from "inferno-router"; import { renderToString } from "inferno-server"; import { matchPath } from "inferno-router"; import path from "path"; import { App } from "../shared/components/app"; import { ILemmyConfig, InitialFetchRequest, IsoData, } from "../shared/interfaces"; import { routes } from "../shared/routes"; import IsomorphicCookie from "isomorphic-cookie"; import { GetSite, GetSiteResponse, LemmyHttp } from "lemmy-js-client"; import process from "process"; import { Helmet } from "inferno-helmet"; import { initializeSite } from "../shared/initialize"; import { httpBase } from "../shared/env"; import { IncomingHttpHeaders } from "http"; import { setOptionalAuth } from "../shared/utils"; const server = express(); const [hostname, port] = process.env["LEMMY_INTERNAL_HOST"] ? process.env["LEMMY_INTERNAL_HOST"].split(":") : ["0.0.0.0", "1234"]; server.use(express.json()); server.use(express.urlencoded({ extended: false })); server.use("/static", express.static(path.resolve("./dist"))); // server.use(cookieParser()); server.get("/*", async (req, res) => { const activeRoute = routes.find(route => matchPath(req.path, route)) || {}; const context = {} as any; let auth: string = IsomorphicCookie.load("jwt", req); let getSiteForm: GetSite = {}; setOptionalAuth(getSiteForm, auth); let promises: Promise[] = []; let headers = setForwardedHeaders(req.headers); let initialFetchReq: InitialFetchRequest = { client: new LemmyHttp(httpBase, headers), auth, path: req.path, }; // Get site data first // This bypasses errors, so that the client can hit the error on its own, // in order to remove the jwt on the browser. Necessary for wrong jwts let try_site: any = await initialFetchReq.client.getSite(getSiteForm); if (try_site.error == "not_logged_in") { console.error( "Incorrect JWT token, skipping auth so frontend can remove jwt cookie" ); delete getSiteForm.auth; delete initialFetchReq.auth; try_site = await initialFetchReq.client.getSite(getSiteForm); } let site: GetSiteResponse = try_site; initializeSite(site); if (activeRoute.fetchInitialData) { promises.push(...activeRoute.fetchInitialData(initialFetchReq)); } let routeData = await Promise.all(promises); // Redirect to the 404 if there's an API error if (routeData[0] && routeData[0].error) { let errCode = routeData[0].error; return res.redirect(`/404?err=${errCode}`); } let acceptLang = req.headers["accept-language"] ? req.headers["accept-language"].split(",")[0] : "en"; let lang = site.my_user ? site.my_user.local_user.lang == "browser" ? acceptLang : "en" : acceptLang; let isoData: IsoData = { path: req.path, site_res: site, routeData, lang, }; const wrapper = ( ); if (context.url) { return res.redirect(context.url); } const cspHtml = ( ); const root = renderToString(wrapper); const cspStr = process.env.LEMMY_EXTERNAL_HOST ? renderToString(cspHtml) : ""; const helmet = Helmet.renderStatic(); const config: ILemmyConfig = { wsHost: process.env.LEMMY_WS_HOST }; res.send(` ${helmet.title.toString()} ${helmet.meta.toString()} ${cspStr} ${helmet.link.toString()}
${root}
`); }); server.listen(Number(port), hostname, () => { console.log(`http://${hostname}:${port}`); }); function setForwardedHeaders( headers: IncomingHttpHeaders ): { [key: string]: string } { let out = { host: headers.host, }; if (headers["x-real-ip"]) { out["x-real-ip"] = headers["x-real-ip"]; } if (headers["x-forwarded-for"]) { out["x-forwarded-for"] = headers["x-forwarded-for"]; } return out; } process.on("SIGINT", () => { console.info("Interrupted"); process.exit(0); });