]> Untitled Git - lemmy-ui.git/blob - src/server/utils/create-ssr-html.tsx
0958588d3aeef3e7924e9c150e58647e3c58adcd
[lemmy-ui.git] / src / server / utils / create-ssr-html.tsx
1 import { getStaticDir } from "@utils/env";
2 import { Helmet } from "inferno-helmet";
3 import { renderToString } from "inferno-server";
4 import serialize from "serialize-javascript";
5 import sharp from "sharp";
6 import { favIconPngUrl, favIconUrl } from "../../shared/config";
7 import { IsoDataOptionalSite } from "../../shared/interfaces";
8 import { buildThemeList } from "./build-themes-list";
9 import { fetchIconPng } from "./fetch-icon-png";
10
11 const customHtmlHeader = process.env["LEMMY_UI_CUSTOM_HTML_HEADER"] || "";
12
13 let appleTouchIcon: string | undefined = undefined;
14
15 export async function createSsrHtml(
16   root: string,
17   isoData: IsoDataOptionalSite,
18   cspNonce: string,
19 ) {
20   const site = isoData.site_res;
21
22   const fallbackTheme = `<link rel="stylesheet" type="text/css" href="/css/themes/${
23     (await buildThemeList())[0]
24   }.css" />`;
25
26   const customHtmlHeaderScriptTag = new RegExp("<script", "g");
27   const customHtmlHeaderWithNonce = customHtmlHeader.replace(
28     customHtmlHeaderScriptTag,
29     `<script nonce="${cspNonce}"`,
30   );
31
32   if (!appleTouchIcon) {
33     appleTouchIcon = site?.site_view.site.icon
34       ? `data:image/png;base64,${await sharp(
35           await fetchIconPng(site.site_view.site.icon),
36         )
37           .resize(180, 180)
38           .extend({
39             bottom: 20,
40             top: 20,
41             left: 20,
42             right: 20,
43             background: "#222222",
44           })
45           .png()
46           .toBuffer()
47           .then(buf => buf.toString("base64"))}`
48       : favIconPngUrl;
49   }
50
51   const erudaStr =
52     process.env["LEMMY_UI_DEBUG"] === "true"
53       ? renderToString(
54           <>
55             <script
56               nonce={cspNonce}
57               src="//cdn.jsdelivr.net/npm/eruda"
58             ></script>
59             <script nonce={cspNonce}>eruda.init();</script>
60           </>,
61         )
62       : "";
63
64   const helmet = Helmet.renderStatic();
65
66   return `
67     <!DOCTYPE html>
68     <html ${helmet.htmlAttributes.toString()}>
69     <head>
70     <script nonce="${cspNonce}">window.isoData = ${serialize(isoData)}</script>
71   
72     <!-- A remote debugging utility for mobile -->
73     ${erudaStr}
74   
75     <!-- Custom injected script -->
76     ${customHtmlHeaderWithNonce}
77   
78     ${helmet.title.toString()}
79     ${helmet.meta.toString()}
80   
81     <!-- Required meta tags -->
82     <meta name="Description" content="Lemmy">
83     <meta charset="utf-8">
84     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
85     <link
86        id="favicon"
87        rel="shortcut icon"
88        type="image/x-icon"
89        href=${site?.site_view.site.icon ?? favIconUrl}
90      />
91   
92     <!-- Web app manifest -->
93     <link rel="manifest" href="/manifest.webmanifest" />
94     <link rel="apple-touch-icon" href=${appleTouchIcon} />
95     <link rel="apple-touch-startup-image" href=${appleTouchIcon} />
96   
97     <!-- Styles -->
98     <link rel="stylesheet" type="text/css" href="${getStaticDir()}/styles/styles.css" />
99   
100     <!-- Current theme and more -->
101     ${helmet.link.toString() || fallbackTheme}
102     
103     </head>
104   
105     <body ${helmet.bodyAttributes.toString()}>
106       <noscript>
107         <div class="alert alert-danger rounded-0" role="alert">
108           <b>Javascript is disabled. Actions will not work.</b>
109         </div>
110       </noscript>
111   
112       <div id='root'>${root}</div>
113       <script defer src='${getStaticDir()}/js/client.js'></script>
114     </body>
115   </html>
116   `;
117 }