mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-04-05 02:49:32 +00:00
Minor improvements
- use a one-line log format - load templates only once - fixed typos - fix reset timezone in ui settings - hide bookmarks headline if there are none
This commit is contained in:
parent
27025715f4
commit
a3569598d8
@ -47,7 +47,7 @@ object ScribeConfigure {
|
|||||||
if (logger.id == scribe.Logger.RootId) {
|
if (logger.id == scribe.Logger.RootId) {
|
||||||
cfg.format match {
|
cfg.format match {
|
||||||
case Format.Fancy =>
|
case Format.Fancy =>
|
||||||
l.withHandler(formatter = Formatter.default, writer = SystemOutWriter)
|
l.withHandler(formatter = Formatter.enhanced, writer = SystemOutWriter)
|
||||||
case Format.Plain =>
|
case Format.Plain =>
|
||||||
l.withHandler(formatter = Formatter.classic, writer = SystemOutWriter)
|
l.withHandler(formatter = Formatter.classic, writer = SystemOutWriter)
|
||||||
case Format.Json =>
|
case Format.Json =>
|
||||||
|
@ -104,7 +104,7 @@ object RestServer {
|
|||||||
)(
|
)(
|
||||||
wsB: WebSocketBuilder2[F]
|
wsB: WebSocketBuilder2[F]
|
||||||
) = {
|
) = {
|
||||||
val templates = TemplateRoutes[F](cfg)
|
val templates = TemplateRoutes[F](cfg, Templates[F])
|
||||||
val httpApp = Router(
|
val httpApp = Router(
|
||||||
"/internal" -> InternalHeader(internSettings.internalRouteKey) {
|
"/internal" -> InternalHeader(internSettings.internalRouteKey) {
|
||||||
internalRoutes(pubSub)
|
internalRoutes(pubSub)
|
||||||
|
@ -6,12 +6,8 @@
|
|||||||
|
|
||||||
package docspell.restserver.webapp
|
package docspell.restserver.webapp
|
||||||
|
|
||||||
import java.net.URL
|
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
|
||||||
|
|
||||||
import cats.effect._
|
import cats.effect._
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import fs2.text
|
|
||||||
|
|
||||||
import docspell.restserver.{BuildInfo, Config}
|
import docspell.restserver.{BuildInfo, Config}
|
||||||
|
|
||||||
@ -36,22 +32,16 @@ object TemplateRoutes {
|
|||||||
def serviceWorker: HttpRoutes[F]
|
def serviceWorker: HttpRoutes[F]
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply[F[_]: Async](cfg: Config): InnerRoutes[F] = {
|
def apply[F[_]: Async](cfg: Config, templates: Templates[F]): InnerRoutes[F] = {
|
||||||
val indexTemplate = memo(
|
|
||||||
loadResource("/index.html").flatMap(loadTemplate(_))
|
|
||||||
)
|
|
||||||
val docTemplate = memo(loadResource("/doc.html").flatMap(loadTemplate(_)))
|
|
||||||
val swTemplate = memo(loadResource("/sw.js").flatMap(loadTemplate(_)))
|
|
||||||
|
|
||||||
val dsl = new Http4sDsl[F] {}
|
val dsl = new Http4sDsl[F] {}
|
||||||
import dsl._
|
import dsl._
|
||||||
new InnerRoutes[F] {
|
new InnerRoutes[F] {
|
||||||
def doc =
|
def doc =
|
||||||
HttpRoutes.of[F] { case GET -> Root =>
|
HttpRoutes.of[F] { case GET -> Root =>
|
||||||
for {
|
for {
|
||||||
templ <- docTemplate
|
docTemplate <- templates.doc
|
||||||
resp <- Ok(
|
resp <- Ok(
|
||||||
DocData().render(templ),
|
DocData().render(docTemplate),
|
||||||
`Content-Type`(textHtml, Charset.`UTF-8`)
|
`Content-Type`(textHtml, Charset.`UTF-8`)
|
||||||
)
|
)
|
||||||
} yield resp
|
} yield resp
|
||||||
@ -59,9 +49,9 @@ object TemplateRoutes {
|
|||||||
def app =
|
def app =
|
||||||
HttpRoutes.of[F] { case GET -> _ =>
|
HttpRoutes.of[F] { case GET -> _ =>
|
||||||
for {
|
for {
|
||||||
templ <- indexTemplate
|
indexTemplate <- templates.index
|
||||||
resp <- Ok(
|
resp <- Ok(
|
||||||
IndexData(cfg).render(templ),
|
IndexData(cfg).render(indexTemplate),
|
||||||
`Content-Type`(textHtml, Charset.`UTF-8`)
|
`Content-Type`(textHtml, Charset.`UTF-8`)
|
||||||
)
|
)
|
||||||
} yield resp
|
} yield resp
|
||||||
@ -70,9 +60,9 @@ object TemplateRoutes {
|
|||||||
def serviceWorker =
|
def serviceWorker =
|
||||||
HttpRoutes.of[F] { case GET -> _ =>
|
HttpRoutes.of[F] { case GET -> _ =>
|
||||||
for {
|
for {
|
||||||
templ <- swTemplate
|
swTemplate <- templates.serviceWorker
|
||||||
resp <- Ok(
|
resp <- Ok(
|
||||||
IndexData(cfg).render(templ),
|
IndexData(cfg).render(swTemplate),
|
||||||
`Content-Type`(appJavascript, Charset.`UTF-8`)
|
`Content-Type`(appJavascript, Charset.`UTF-8`)
|
||||||
)
|
)
|
||||||
} yield resp
|
} yield resp
|
||||||
@ -80,31 +70,6 @@ object TemplateRoutes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def loadResource[F[_]: Sync](name: String): F[URL] =
|
|
||||||
Option(getClass.getResource(name)) match {
|
|
||||||
case None =>
|
|
||||||
Sync[F].raiseError(new Exception("Unknown resource: " + name))
|
|
||||||
case Some(r) =>
|
|
||||||
r.pure[F]
|
|
||||||
}
|
|
||||||
|
|
||||||
def loadUrl[F[_]: Sync](url: URL): F[String] =
|
|
||||||
fs2.io
|
|
||||||
.readInputStream(Sync[F].delay(url.openStream()), 64 * 1024)
|
|
||||||
.through(text.utf8.decode)
|
|
||||||
.compile
|
|
||||||
.string
|
|
||||||
|
|
||||||
def parseTemplate[F[_]: Sync](str: String): F[Template] =
|
|
||||||
Sync[F].pure(mustache.parse(str).leftMap(err => new Exception(err._2))).rethrow
|
|
||||||
|
|
||||||
def loadTemplate[F[_]: Sync](url: URL): F[Template] = {
|
|
||||||
val logger = docspell.logging.getLogger[F]
|
|
||||||
loadUrl[F](url).flatMap(parseTemplate[F]).flatMap { t =>
|
|
||||||
logger.info(s"Compiled template $url") *> t.pure[F]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class DocData(swaggerRoot: String, openapiSpec: String)
|
case class DocData(swaggerRoot: String, openapiSpec: String)
|
||||||
object DocData {
|
object DocData {
|
||||||
|
|
||||||
@ -150,18 +115,4 @@ object TemplateRoutes {
|
|||||||
implicit def yamuscaValueConverter: ValueConverter[IndexData] =
|
implicit def yamuscaValueConverter: ValueConverter[IndexData] =
|
||||||
deriveValueConverter[IndexData]
|
deriveValueConverter[IndexData]
|
||||||
}
|
}
|
||||||
|
|
||||||
private def memo[F[_]: Sync, A](fa: => F[A]): F[A] = {
|
|
||||||
val ref = new AtomicReference[A]()
|
|
||||||
Sync[F].defer {
|
|
||||||
Option(ref.get) match {
|
|
||||||
case Some(a) => a.pure[F]
|
|
||||||
case None =>
|
|
||||||
fa.map { a =>
|
|
||||||
ref.set(a)
|
|
||||||
a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Eike K. & Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package docspell.restserver.webapp
|
||||||
|
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
import cats.effect._
|
||||||
|
import cats.effect.unsafe.implicits._
|
||||||
|
import cats.implicits._
|
||||||
|
import fs2.text
|
||||||
|
|
||||||
|
import yamusca.imports._
|
||||||
|
|
||||||
|
trait Templates[F[_]] {
|
||||||
|
def index: F[Template]
|
||||||
|
def doc: F[Template]
|
||||||
|
def serviceWorker: F[Template]
|
||||||
|
}
|
||||||
|
|
||||||
|
object Templates {
|
||||||
|
|
||||||
|
def apply[F[_]: Sync]: Templates[F] =
|
||||||
|
new Templates[F] {
|
||||||
|
def index = Templates.indexTemplate.pure[F]
|
||||||
|
def doc = Templates.docTemplate.pure[F]
|
||||||
|
def serviceWorker = Templates.swTemplate.pure[F]
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy val indexTemplate = fromResource[IO]("/index.html").unsafeRunSync()
|
||||||
|
private lazy val docTemplate = fromResource[IO]("/doc.html").unsafeRunSync()
|
||||||
|
private lazy val swTemplate = fromResource[IO]("/sw.js").unsafeRunSync()
|
||||||
|
|
||||||
|
def fromResource[F[_]: Sync](path: String): F[Template] =
|
||||||
|
loadResource[F](path).flatMap(loadTemplate[F](_))
|
||||||
|
|
||||||
|
private def loadResource[F[_]: Sync](name: String): F[URL] =
|
||||||
|
Option(getClass.getResource(name)) match {
|
||||||
|
case None =>
|
||||||
|
Sync[F].raiseError(new Exception("Unknown resource: " + name))
|
||||||
|
case Some(r) =>
|
||||||
|
r.pure[F]
|
||||||
|
}
|
||||||
|
|
||||||
|
private def loadUrl[F[_]: Sync](url: URL): F[String] =
|
||||||
|
fs2.io
|
||||||
|
.readInputStream(Sync[F].delay(url.openStream()), 64 * 1024)
|
||||||
|
.through(text.utf8.decode)
|
||||||
|
.compile
|
||||||
|
.string
|
||||||
|
|
||||||
|
private def parseTemplate[F[_]: Sync](str: String): F[Template] =
|
||||||
|
Sync[F].pure(mustache.parse(str).leftMap(err => new Exception(err._2))).rethrow
|
||||||
|
|
||||||
|
private def loadTemplate[F[_]: Sync](url: URL): F[Template] = {
|
||||||
|
val logger = docspell.logging.getLogger[F]
|
||||||
|
loadUrl[F](url).flatMap(parseTemplate[F]).flatMap { t =>
|
||||||
|
logger.info(s"Compiled template $url") *> t.pure[F]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -308,7 +308,7 @@ view texts settings model =
|
|||||||
(Comp.EventSample.viewJson texts.eventSample False model.eventSampleModel)
|
(Comp.EventSample.viewJson texts.eventSample False model.eventSampleModel)
|
||||||
]
|
]
|
||||||
, div [ class "mt-4" ]
|
, div [ class "mt-4" ]
|
||||||
[ formHeader "Test Delviery"
|
[ formHeader "Test Delivery"
|
||||||
, Html.map DeliveryTestMsg
|
, Html.map DeliveryTestMsg
|
||||||
(Comp.NotificationTest.view
|
(Comp.NotificationTest.view
|
||||||
{ runDisabled = getHook model == Nothing }
|
{ runDisabled = getHook model == Nothing }
|
||||||
|
@ -569,7 +569,7 @@ update flags sett msg model =
|
|||||||
newSettings =
|
newSettings =
|
||||||
case tab of
|
case tab of
|
||||||
GeneralTab ->
|
GeneralTab ->
|
||||||
{ sett | uiLang = Nothing, sideMenuVisible = Nothing }
|
{ sett | uiLang = Nothing, timeZone = Nothing, sideMenuVisible = Nothing }
|
||||||
|
|
||||||
SearchTab ->
|
SearchTab ->
|
||||||
{ sett
|
{ sett
|
||||||
|
@ -31,20 +31,22 @@ view texts versionInfo _ model =
|
|||||||
, menuLink [ Page.href (SearchPage Nothing) ] (Icons.searchIcon "") texts.basics.items
|
, menuLink [ Page.href (SearchPage Nothing) ] (Icons.searchIcon "") texts.basics.items
|
||||||
, menuLink [ onClick InitUpload, href "#" ] (Icons.fileUploadIcon "") texts.uploadFiles
|
, menuLink [ onClick InitUpload, href "#" ] (Icons.fileUploadIcon "") texts.uploadFiles
|
||||||
]
|
]
|
||||||
, h3
|
, div [ classList [ ( "hidden", Comp.BookmarkChooser.isEmpty model.sideMenu.bookmarkChooser ) ] ]
|
||||||
[ class S.header3
|
[ h3
|
||||||
, class "italic mt-3"
|
[ class S.header3
|
||||||
]
|
, class "italic mt-3"
|
||||||
[ text texts.bookmarks
|
]
|
||||||
]
|
[ text texts.bookmarks
|
||||||
, div [ class "ml-2" ]
|
]
|
||||||
[ Html.map BookmarkMsg
|
, div [ class "ml-2" ]
|
||||||
(Comp.BookmarkChooser.viewWith
|
[ Html.map BookmarkMsg
|
||||||
{ showUser = True, showCollective = True, showShares = False }
|
(Comp.BookmarkChooser.viewWith
|
||||||
texts.bookmarkChooser
|
{ showUser = True, showCollective = True, showShares = False }
|
||||||
model.sideMenu.bookmarkChooser
|
texts.bookmarkChooser
|
||||||
Comp.BookmarkChooser.emptySelection
|
model.sideMenu.bookmarkChooser
|
||||||
)
|
Comp.BookmarkChooser.emptySelection
|
||||||
|
)
|
||||||
|
]
|
||||||
]
|
]
|
||||||
, h3
|
, h3
|
||||||
[ class S.header3
|
[ class S.header3
|
||||||
|
Loading…
x
Reference in New Issue
Block a user