@@ -19,6 +19,7 @@ import org.http4s.server.middleware.Logger
1919import org .http4s .server .staticcontent .*
2020import org .http4s .server .staticcontent .FileService
2121import org .typelevel .ci .CIStringSyntax
22+ import org .http4s .EntityBody
2223
2324import fs2 .*
2425import fs2 .concurrent .Topic
@@ -36,6 +37,7 @@ import cats.effect.kernel.Resource
3637import cats .syntax .all .*
3738
3839import _root_ .io .circe .syntax .EncoderOps
40+ import org .http4s .Http
3941
4042def routes [F [_]: Files : MonadThrow ](
4143 stringPath : String ,
@@ -70,8 +72,8 @@ def routes[F[_]: Files: MonadThrow](
7072 ref
7173 )(logger)
7274
73- val hashFalse = vanillaTemplate(false ).render.hashCode.toString
74- val hashTrue = vanillaTemplate(true ).render.hashCode.toString
75+ // val hashFalse = vanillaTemplate(false).render.hashCode.toString
76+ // val hashTrue = vanillaTemplate(true).render.hashCode.toString
7577 val zdt = ZonedDateTime .now()
7678
7779 def userBrowserCacheHeaders (resp : Response [IO ], lastModZdt : ZonedDateTime , injectStyles : Boolean ) =
@@ -97,73 +99,95 @@ def routes[F[_]: Files: MonadThrow](
9799 object StaticHtmlMiddleware :
98100 def apply (service : HttpRoutes [IO ], injectStyles : Boolean )(logger : Scribe [IO ]): HttpRoutes [IO ] = Kleisli {
99101 (req : Request [IO ]) =>
100- req.headers.get(ci " If-None-Match " ).map(_.toList) match
101- case Some (h :: Nil ) if h.value == hashFalse => OptionT .liftF(IO (Response [IO ](Status .NotModified )))
102- case Some (h :: Nil ) if h.value == hashTrue => OptionT .liftF(IO (Response [IO ](Status .NotModified )))
103- case _ => service(req).semiflatMap(userBrowserCacheHeaders(_, zdt, injectStyles))
104- end match
105-
102+ service(req).semiflatMap(userBrowserCacheHeaders(_, zdt, injectStyles))
106103 }
107104
108105 end StaticHtmlMiddleware
109106
110- def generatedIndexHtml (injectStyles : Boolean ) =
107+ def generatedIndexHtml (injectStyles : Boolean , modules : Ref [ IO , Map [ String , String ]] ) =
111108 StaticHtmlMiddleware (
112109 HttpRoutes .of[IO ] {
113110 case req @ GET -> Root =>
114111 logger.trace(" Generated index.html" ) >>
115- IO (
112+ vanillaTemplate(injectStyles, modules).map : html =>
116113 Response [IO ]()
117- .withEntity(vanillaTemplate(injectStyles) )
114+ .withEntity(html )
118115 .withHeaders(
119116 Header .Raw (ci " Cache-Control " , " no-cache" ),
120117 Header .Raw (
121118 ci " ETag " ,
122- injectStyles match
123- case true => hashTrue
124- case false => hashFalse
119+ html.hashCode.toString
125120 ),
126121 Header .Raw (ci " Last-Modified " , formatter.format(zdt)),
127122 Header .Raw (
128123 ci " Expires " ,
129124 httpCacheFormat(ZonedDateTime .ofInstant(Instant .now().plusSeconds(10000000 ), ZoneId .of(" GMT" )))
130125 )
131126 )
132- )
127+
133128 },
134129 injectStyles
135130 )(logger).combineK(
136131 StaticHtmlMiddleware (
137132 HttpRoutes .of[IO ] {
138133 case GET -> Root / " index.html" =>
139- IO {
140- Response [IO ]().withEntity(vanillaTemplate(injectStyles) )
141- }
134+ vanillaTemplate(injectStyles, modules).map : html =>
135+ Response [IO ]().withEntity(html )
136+
142137 },
143138 injectStyles
144139 )(logger)
145140 )
146141
147142 // val formatter = DateTimeFormatter.RFC_1123_DATE_TIME
148- val staticAssetRoutes : HttpRoutes [IO ] = indexOpts match
149- case None => generatedIndexHtml(injectStyles = false )
143+ def staticAssetRoutes ( modules : Ref [ IO , Map [ String , String ]]) : HttpRoutes [IO ] = indexOpts match
144+ case None => generatedIndexHtml(injectStyles = false , modules )
150145
151146 case Some (IndexHtmlConfig .IndexHtmlPath (path)) =>
152- StaticMiddleware (
153- Router (
154- " " -> fileService[IO ](FileService .Config (path.toString()))
155- ),
156- fs2.io.file.Path (path.toString())
157- )(logger)
147+ // StaticMiddleware(
148+ // Router(
149+ // "" ->
150+ HttpRoutes
151+ .of[IO ] {
152+ case req @ GET -> Root =>
153+ StaticFile
154+ .fromPath[IO ](path / " index.html" )
155+ .getOrElseF(NotFound ())
156+ .flatMap {
157+ f =>
158+ f.body
159+ .through(text.utf8.decode)
160+ .compile
161+ .string
162+ .flatMap {
163+ body =>
164+ for str <- injectModulePreloads(modules, body)
165+ yield
166+ val bytes = str.getBytes()
167+ f.withEntity(bytes)
168+ Response [IO ]().withEntity(bytes).putHeaders(" Content-Type" -> " text/html" )
169+
170+ }
171+ }
172+
173+ }
174+ .combineK(
175+ StaticMiddleware (
176+ Router (
177+ " " -> fileService[IO ](FileService .Config (path.toString()))
178+ ),
179+ fs2.io.file.Path (path.toString())
180+ )(logger)
181+ )
158182
159183 case Some (IndexHtmlConfig .StylesOnly (stylesPath)) =>
160184 NoCacheMiddlware (
161185 Router (
162186 " " -> fileService[IO ](FileService .Config (stylesPath.toString()))
163187 )
164- )(logger).combineK(generatedIndexHtml(injectStyles = true ))
188+ )(logger).combineK(generatedIndexHtml(injectStyles = true , modules ))
165189
166- val clientSpaRoutes : HttpRoutes [IO ] =
190+ def clientSpaRoutes ( modules : Ref [ IO , Map [ String , String ]]) : HttpRoutes [IO ] =
167191 clientRoutingPrefix match
168192 case None => HttpRoutes .empty[IO ]
169193 case Some (spaRoute) =>
@@ -173,9 +197,9 @@ def routes[F[_]: Files: MonadThrow](
173197 StaticHtmlMiddleware (
174198 HttpRoutes .of[IO ] {
175199 case req @ GET -> root /: path =>
176- IO (
177- Response [IO ]().withEntity(vanillaTemplate( false ) )
178- )
200+ vanillaTemplate( false , modules).map : html =>
201+ Response [IO ]().withEntity(html )
202+
179203 },
180204 false
181205 )(logger)
@@ -184,9 +208,8 @@ def routes[F[_]: Files: MonadThrow](
184208 StaticHtmlMiddleware (
185209 HttpRoutes .of[IO ] {
186210 case GET -> root /: spaRoute /: path =>
187- IO (
188- Response [IO ]().withEntity(vanillaTemplate(true ))
189- )
211+ vanillaTemplate(true , modules).map: html =>
212+ Response [IO ]().withEntity(html)
190213 },
191214 true
192215 )(logger)
@@ -195,7 +218,24 @@ def routes[F[_]: Files: MonadThrow](
195218 StaticFileMiddleware (
196219 HttpRoutes .of[IO ] {
197220 case req @ GET -> spaRoute /: path =>
198- StaticFile .fromPath(dir / " index.html" , Some (req)).getOrElseF(NotFound ())
221+ StaticFile
222+ .fromPath(dir / " index.html" , Some (req))
223+ .getOrElseF(NotFound ())
224+ .flatMap {
225+ f =>
226+ f.body
227+ .through(text.utf8.decode)
228+ .compile
229+ .string
230+ .flatMap: body =>
231+ for str <- injectModulePreloads(modules, body)
232+ yield
233+ val bytes = str.getBytes()
234+ f.withEntity(bytes)
235+ f
236+
237+ }
238+
199239 },
200240 dir / " index.html"
201241 )(logger)
@@ -215,8 +255,8 @@ def routes[F[_]: Files: MonadThrow](
215255 refreshRoutes
216256 .combineK(linkedAppWithCaching)
217257 .combineK(proxyRoutes)
218- .combineK(clientSpaRoutes)
219- .combineK(staticAssetRoutes)
258+ .combineK(clientSpaRoutes(ref) )
259+ .combineK(staticAssetRoutes(ref) )
220260 )
221261
222262 clientRoutingPrefix.fold(IO .unit)(s => logger.trace(s " client spa at : $s" )).toResource >>
0 commit comments