mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 10:28:27 +00:00
Upgrade code base to CE3
This commit is contained in:
@ -1,12 +1,12 @@
|
||||
package docspell.restserver
|
||||
|
||||
import java.net.InetAddress
|
||||
|
||||
import docspell.backend.auth.Login
|
||||
import docspell.backend.{Config => BackendConfig}
|
||||
import docspell.common._
|
||||
import docspell.ftssolr.SolrConfig
|
||||
|
||||
import com.comcast.ip4s.IpAddress
|
||||
|
||||
case class Config(
|
||||
appName: String,
|
||||
appId: Ident,
|
||||
@ -42,12 +42,14 @@ object Config {
|
||||
case class HttpHeader(enabled: Boolean, headerName: String, headerValue: String)
|
||||
case class AllowedIps(enabled: Boolean, ips: Set[String]) {
|
||||
|
||||
def containsAddress(inet: InetAddress): Boolean = {
|
||||
val ip = inet.getHostAddress
|
||||
def containsAddress(inet: IpAddress): Boolean = {
|
||||
val ip = inet.fold(_.toUriString, _.toUriString) //.getHostAddress
|
||||
lazy val ipParts = ip.split('.')
|
||||
|
||||
def checkSingle(pattern: String): Boolean =
|
||||
pattern == ip || (inet.isLoopbackAddress && pattern == "127.0.0.1") || (pattern
|
||||
pattern == ip || (ip.contains(
|
||||
"localhost"
|
||||
) && pattern == "127.0.0.1") || (pattern
|
||||
.split('.')
|
||||
.zip(ipParts)
|
||||
.foldLeft(true) { case (r, (a, b)) =>
|
||||
|
@ -52,9 +52,8 @@ object Main extends IOApp {
|
||||
val pools = for {
|
||||
cec <- connectEC
|
||||
bec <- blockingEC
|
||||
blocker = Blocker.liftExecutorService(bec)
|
||||
rec <- restserverEC
|
||||
} yield Pools(cec, bec, blocker, rec)
|
||||
} yield Pools(cec, bec, rec)
|
||||
|
||||
logger.info(s"\n${banner.render("***>")}")
|
||||
if (EnvMode.current.isDev) {
|
||||
|
@ -24,21 +24,20 @@ final class RestAppImpl[F[_]](val config: Config, val backend: BackendApp[F])
|
||||
|
||||
object RestAppImpl {
|
||||
|
||||
def create[F[_]: ConcurrentEffect: ContextShift](
|
||||
def create[F[_]: Async](
|
||||
cfg: Config,
|
||||
connectEC: ExecutionContext,
|
||||
httpClientEc: ExecutionContext,
|
||||
blocker: Blocker
|
||||
httpClientEc: ExecutionContext
|
||||
): Resource[F, RestApp[F]] =
|
||||
for {
|
||||
backend <- BackendApp(cfg.backend, connectEC, httpClientEc, blocker)(
|
||||
backend <- BackendApp(cfg.backend, connectEC, httpClientEc)(
|
||||
createFtsClient[F](cfg)
|
||||
)
|
||||
app = new RestAppImpl[F](cfg, backend)
|
||||
appR <- Resource.make(app.init.map(_ => app))(_.shutdown)
|
||||
} yield appR
|
||||
|
||||
private def createFtsClient[F[_]: ConcurrentEffect](
|
||||
private def createFtsClient[F[_]: Async](
|
||||
cfg: Config
|
||||
)(client: Client[F]): Resource[F, FtsClient[F]] =
|
||||
if (cfg.fullTextSearch.enabled) SolrFtsClient(cfg.fullTextSearch.solr, client)
|
||||
|
@ -11,36 +11,33 @@ import docspell.restserver.routes._
|
||||
import docspell.restserver.webapp._
|
||||
|
||||
import org.http4s._
|
||||
import org.http4s.blaze.server.BlazeServerBuilder
|
||||
import org.http4s.dsl.Http4sDsl
|
||||
import org.http4s.headers.Location
|
||||
import org.http4s.implicits._
|
||||
import org.http4s.server.Router
|
||||
import org.http4s.server.blaze.BlazeServerBuilder
|
||||
import org.http4s.server.middleware.Logger
|
||||
|
||||
object RestServer {
|
||||
|
||||
def stream[F[_]: ConcurrentEffect](
|
||||
cfg: Config,
|
||||
pools: Pools
|
||||
)(implicit T: Timer[F], CS: ContextShift[F]): Stream[F, Nothing] = {
|
||||
def stream[F[_]: Async](cfg: Config, pools: Pools): Stream[F, Nothing] = {
|
||||
|
||||
val templates = TemplateRoutes[F](pools.blocker, cfg)
|
||||
val templates = TemplateRoutes[F](cfg)
|
||||
val app = for {
|
||||
restApp <-
|
||||
RestAppImpl
|
||||
.create[F](cfg, pools.connectEC, pools.httpClientEC, pools.blocker)
|
||||
.create[F](cfg, pools.connectEC, pools.httpClientEC)
|
||||
httpApp = Router(
|
||||
"/api/info" -> routes.InfoRoutes(),
|
||||
"/api/v1/open/" -> openRoutes(cfg, restApp),
|
||||
"/api/v1/sec/" -> Authenticate(restApp.backend.login, cfg.auth) { token =>
|
||||
securedRoutes(cfg, pools, restApp, token)
|
||||
securedRoutes(cfg, restApp, token)
|
||||
},
|
||||
"/api/v1/admin" -> AdminRoutes(cfg.adminEndpoint) {
|
||||
adminRoutes(cfg, restApp)
|
||||
},
|
||||
"/api/doc" -> templates.doc,
|
||||
"/app/assets" -> EnvMiddleware(WebjarRoutes.appRoutes[F](pools.blocker)),
|
||||
"/app/assets" -> EnvMiddleware(WebjarRoutes.appRoutes[F]),
|
||||
"/app" -> EnvMiddleware(templates.app),
|
||||
"/sw.js" -> EnvMiddleware(templates.serviceWorker),
|
||||
"/" -> redirectTo("/app")
|
||||
@ -61,9 +58,8 @@ object RestServer {
|
||||
)
|
||||
}.drain
|
||||
|
||||
def securedRoutes[F[_]: Effect: ContextShift](
|
||||
def securedRoutes[F[_]: Async](
|
||||
cfg: Config,
|
||||
pools: Pools,
|
||||
restApp: RestApp[F],
|
||||
token: AuthToken
|
||||
): HttpRoutes[F] =
|
||||
@ -77,9 +73,9 @@ object RestServer {
|
||||
"user" -> UserRoutes(restApp.backend, token),
|
||||
"collective" -> CollectiveRoutes(restApp.backend, token),
|
||||
"queue" -> JobQueueRoutes(restApp.backend, token),
|
||||
"item" -> ItemRoutes(cfg, pools.blocker, restApp.backend, token),
|
||||
"item" -> ItemRoutes(cfg, restApp.backend, token),
|
||||
"items" -> ItemMultiRoutes(restApp.backend, token),
|
||||
"attachment" -> AttachmentRoutes(pools.blocker, restApp.backend, token),
|
||||
"attachment" -> AttachmentRoutes(restApp.backend, token),
|
||||
"attachments" -> AttachmentMultiRoutes(restApp.backend, token),
|
||||
"upload" -> UploadRoutes.secured(restApp.backend, cfg, token),
|
||||
"checkfile" -> CheckFileRoutes.secured(restApp.backend, token),
|
||||
@ -95,7 +91,7 @@ object RestServer {
|
||||
"clientSettings" -> ClientSettingsRoutes(restApp.backend, token)
|
||||
)
|
||||
|
||||
def openRoutes[F[_]: Effect](cfg: Config, restApp: RestApp[F]): HttpRoutes[F] =
|
||||
def openRoutes[F[_]: Async](cfg: Config, restApp: RestApp[F]): HttpRoutes[F] =
|
||||
Router(
|
||||
"auth" -> LoginRoutes.login(restApp.backend.login, cfg),
|
||||
"signup" -> RegisterRoutes(restApp.backend, cfg),
|
||||
@ -104,14 +100,14 @@ object RestServer {
|
||||
"integration" -> IntegrationEndpointRoutes.open(restApp.backend, cfg)
|
||||
)
|
||||
|
||||
def adminRoutes[F[_]: Effect](cfg: Config, restApp: RestApp[F]): HttpRoutes[F] =
|
||||
def adminRoutes[F[_]: Async](cfg: Config, restApp: RestApp[F]): HttpRoutes[F] =
|
||||
Router(
|
||||
"fts" -> FullTextIndexRoutes.admin(cfg, restApp.backend),
|
||||
"user" -> UserRoutes.admin(restApp.backend),
|
||||
"info" -> InfoRoutes.admin(cfg)
|
||||
)
|
||||
|
||||
def redirectTo[F[_]: Effect](path: String): HttpRoutes[F] = {
|
||||
def redirectTo[F[_]: Async](path: String): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
@ -119,7 +115,7 @@ object RestServer {
|
||||
Response[F](
|
||||
Status.SeeOther,
|
||||
body = Stream.empty,
|
||||
headers = Headers.of(Location(Uri(path = path)))
|
||||
headers = Headers(Location(Uri(path = Uri.Path.unsafeFromString(path))))
|
||||
).pure[F]
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import docspell.common.AccountId
|
||||
import docspell.common.LenientUri
|
||||
|
||||
import org.http4s._
|
||||
import org.http4s.util._
|
||||
import org.typelevel.ci.CIString
|
||||
|
||||
case class CookieData(auth: AuthToken) {
|
||||
def accountId: AccountId = auth.account
|
||||
@ -37,7 +37,7 @@ object CookieData {
|
||||
|
||||
def fromCookie[F[_]](req: Request[F]): Either[String, String] =
|
||||
for {
|
||||
header <- headers.Cookie.from(req.headers).toRight("Cookie parsing error")
|
||||
header <- req.headers.get[headers.Cookie].toRight("Cookie parsing error")
|
||||
cookie <-
|
||||
header.values.toList
|
||||
.find(_.name == cookieName)
|
||||
@ -46,8 +46,8 @@ object CookieData {
|
||||
|
||||
def fromHeader[F[_]](req: Request[F]): Either[String, String] =
|
||||
req.headers
|
||||
.get(CaseInsensitiveString(headerName))
|
||||
.map(_.value)
|
||||
.get(CIString(headerName))
|
||||
.map(_.head.value)
|
||||
.toRight("Couldn't find an authenticator")
|
||||
|
||||
def deleteCookie(baseUrl: LenientUri): ResponseCookie =
|
||||
|
@ -33,7 +33,7 @@ object RememberCookieData {
|
||||
|
||||
def fromCookie[F[_]](req: Request[F]): Option[String] =
|
||||
for {
|
||||
header <- headers.Cookie.from(req.headers)
|
||||
header <- req.headers.get[headers.Cookie]
|
||||
cookie <- header.values.toList.find(_.name == cookieName)
|
||||
} yield cookie.content
|
||||
|
||||
|
@ -2,7 +2,7 @@ package docspell.restserver.conv
|
||||
|
||||
import java.time.{LocalDate, ZoneId}
|
||||
|
||||
import cats.effect.{Effect, Sync}
|
||||
import cats.effect.{Async, Sync}
|
||||
import cats.implicits._
|
||||
import fs2.Stream
|
||||
|
||||
@ -294,7 +294,7 @@ trait Conversions {
|
||||
JobLogEvent(jl.created, jl.level, jl.message)
|
||||
|
||||
// upload
|
||||
def readMultipart[F[_]: Effect](
|
||||
def readMultipart[F[_]: Async](
|
||||
mp: Multipart[F],
|
||||
sourceName: String,
|
||||
logger: Logger,
|
||||
@ -347,11 +347,11 @@ trait Conversions {
|
||||
.filter(p => p.name.forall(s => !s.equalsIgnoreCase("meta")))
|
||||
.map(p =>
|
||||
OUpload
|
||||
.File(p.filename, p.headers.get(`Content-Type`).map(fromContentType), p.body)
|
||||
.File(p.filename, p.headers.get[`Content-Type`].map(fromContentType), p.body)
|
||||
)
|
||||
for {
|
||||
metaData <- meta
|
||||
_ <- Effect[F].delay(logger.debug(s"Parsed upload meta data: $metaData"))
|
||||
_ <- Async[F].delay(logger.debug(s"Parsed upload meta data: $metaData"))
|
||||
tracker <- Ident.randomId[F]
|
||||
} yield UploadData(metaData._1, metaData._2, files, prio, Some(tracker))
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import org.http4s.circe.CirceEntityEncoder._
|
||||
import org.http4s.dsl.Http4sDsl
|
||||
import org.http4s.headers.ETag.EntityTag
|
||||
import org.http4s.headers._
|
||||
import org.typelevel.ci.CIString
|
||||
|
||||
object BinaryUtil {
|
||||
|
||||
@ -21,12 +22,15 @@ object BinaryUtil {
|
||||
): F[Response[F]] = {
|
||||
import dsl._
|
||||
|
||||
val mt = MediaType.unsafeParse(data.meta.mimetype.asString)
|
||||
val ctype = `Content-Type`(mt)
|
||||
val cntLen: Header = `Content-Length`.unsafeFromLong(data.meta.length)
|
||||
val eTag: Header = ETag(data.meta.checksum)
|
||||
val disp: Header =
|
||||
`Content-Disposition`("inline", Map("filename" -> data.name.getOrElse("")))
|
||||
val mt = MediaType.unsafeParse(data.meta.mimetype.asString)
|
||||
val ctype = `Content-Type`(mt)
|
||||
val cntLen = `Content-Length`.unsafeFromLong(data.meta.length)
|
||||
val eTag = ETag(data.meta.checksum)
|
||||
val disp =
|
||||
`Content-Disposition`(
|
||||
"inline",
|
||||
Map(CIString("filename") -> data.name.getOrElse(""))
|
||||
)
|
||||
|
||||
resp.map(r =>
|
||||
if (r.status == NotModified) r.withHeaders(ctype, eTag, disp)
|
||||
@ -52,13 +56,9 @@ object BinaryUtil {
|
||||
false
|
||||
}
|
||||
|
||||
def noPreview[F[_]: Sync: ContextShift](
|
||||
blocker: Blocker,
|
||||
req: Option[Request[F]]
|
||||
): OptionT[F, Response[F]] =
|
||||
def noPreview[F[_]: Async](req: Option[Request[F]]): OptionT[F, Response[F]] =
|
||||
StaticFile.fromResource(
|
||||
name = "/docspell/restserver/no-preview.svg",
|
||||
blocker = blocker,
|
||||
req = req,
|
||||
preferGzipped = true,
|
||||
classloader = getClass.getClassLoader().some
|
||||
|
@ -8,7 +8,7 @@ import docspell.restserver.Config
|
||||
|
||||
import org.http4s._
|
||||
import org.http4s.headers._
|
||||
import org.http4s.util.CaseInsensitiveString
|
||||
import org.typelevel.ci.CIString
|
||||
|
||||
/** Obtain information about the client by inspecting the request.
|
||||
*/
|
||||
@ -35,23 +35,23 @@ object ClientRequestInfo {
|
||||
xForwardedProto(req).orElse(clientConnectionProto(req))
|
||||
|
||||
private def host[F[_]](req: Request[F]): Option[String] =
|
||||
req.headers.get(Host).map(_.host)
|
||||
req.headers.get[Host].map(_.host)
|
||||
|
||||
private def xForwardedFor[F[_]](req: Request[F]): Option[String] =
|
||||
req.headers
|
||||
.get(`X-Forwarded-For`)
|
||||
.get[`X-Forwarded-For`]
|
||||
.flatMap(_.values.head)
|
||||
.flatMap(inet => Option(inet.getHostName).orElse(Option(inet.getHostAddress)))
|
||||
.map(ip => ip.fold(_.toUriString, _.toUriString))
|
||||
|
||||
private def xForwardedHost[F[_]](req: Request[F]): Option[String] =
|
||||
req.headers
|
||||
.get(CaseInsensitiveString("X-Forwarded-Host"))
|
||||
.map(_.value)
|
||||
.get(CIString("X-Forwarded-Host"))
|
||||
.map(_.head.value)
|
||||
|
||||
private def xForwardedProto[F[_]](req: Request[F]): Option[String] =
|
||||
req.headers
|
||||
.get(CaseInsensitiveString("X-Forwarded-Proto"))
|
||||
.map(_.value)
|
||||
.get(CIString("X-Forwarded-Proto"))
|
||||
.map(_.head.value)
|
||||
|
||||
private def clientConnectionProto[F[_]](req: Request[F]): Option[String] =
|
||||
req.isSecure.map {
|
||||
@ -61,8 +61,8 @@ object ClientRequestInfo {
|
||||
|
||||
private def xForwardedPort[F[_]](req: Request[F]): Option[Int] =
|
||||
req.headers
|
||||
.get(CaseInsensitiveString("X-Forwarded-Port"))
|
||||
.map(_.value)
|
||||
.get(CIString("X-Forwarded-Port"))
|
||||
.map(_.head.value)
|
||||
.flatMap(str => Either.catchNonFatal(str.toInt).toOption)
|
||||
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import org.http4s._
|
||||
import org.http4s.headers._
|
||||
|
||||
object NoCacheMiddleware {
|
||||
private val noCacheHeader: Header =
|
||||
private val noCacheHeader =
|
||||
`Cache-Control`(
|
||||
NonEmptyList.of(
|
||||
CacheDirective.`max-age`(Duration.zero.toScala),
|
||||
|
@ -10,7 +10,7 @@ trait ResponseGenerator[F[_]] {
|
||||
self: Http4sDsl[F] =>
|
||||
|
||||
implicit final class EitherResponses[A, B](e: Either[A, B]) {
|
||||
def toResponse(headers: Header*)(implicit
|
||||
def toResponse(headers: Header.ToRaw*)(implicit
|
||||
F: Applicative[F],
|
||||
w0: EntityEncoder[F, A],
|
||||
w1: EntityEncoder[F, B]
|
||||
@ -23,7 +23,7 @@ trait ResponseGenerator[F[_]] {
|
||||
|
||||
implicit final class OptionResponse[A](o: Option[A]) {
|
||||
def toResponse(
|
||||
headers: Header*
|
||||
headers: Header.ToRaw*
|
||||
)(implicit F: Applicative[F], w0: EntityEncoder[F, A]): F[Response[F]] =
|
||||
o.map(a => Ok(a)).getOrElse(NotFound()).map(_.withHeaders(headers: _*))
|
||||
}
|
||||
|
@ -26,10 +26,10 @@ object Responses {
|
||||
)
|
||||
|
||||
def forbidden[F[_]]: Response[F] =
|
||||
pureForbidden.copy(body = pureForbidden.body.covary[F])
|
||||
pureForbidden.covary[F].copy(body = pureForbidden.body.covary[F])
|
||||
|
||||
def unauthorized[F[_]]: Response[F] =
|
||||
pureUnauthorized.copy(body = pureUnauthorized.body.covary[F])
|
||||
pureUnauthorized.covary[F].copy(body = pureUnauthorized.body.covary[F])
|
||||
|
||||
def noCache[F[_]](r: Response[F]): Response[F] =
|
||||
r.withHeaders(
|
||||
|
@ -11,12 +11,12 @@ import org.http4s._
|
||||
import org.http4s.circe.CirceEntityEncoder._
|
||||
import org.http4s.dsl.Http4sDsl
|
||||
import org.http4s.server._
|
||||
import org.http4s.util.CaseInsensitiveString
|
||||
import org.typelevel.ci.CIString
|
||||
|
||||
object AdminRoutes {
|
||||
private val adminHeader = CaseInsensitiveString("Docspell-Admin-Secret")
|
||||
private val adminHeader = CIString("Docspell-Admin-Secret")
|
||||
|
||||
def apply[F[_]: Effect](cfg: Config.AdminEndpoint)(
|
||||
def apply[F[_]: Async](cfg: Config.AdminEndpoint)(
|
||||
f: HttpRoutes[F]
|
||||
): HttpRoutes[F] = {
|
||||
val dsl: Http4sDsl[F] = new Http4sDsl[F] {}
|
||||
@ -34,7 +34,7 @@ object AdminRoutes {
|
||||
else middleware(AuthedRoutes(authReq => f.run(authReq.req)))
|
||||
}
|
||||
|
||||
private def checkSecret[F[_]: Effect](
|
||||
private def checkSecret[F[_]: Async](
|
||||
cfg: Config.AdminEndpoint
|
||||
): Kleisli[F, Request[F], Either[String, Unit]] =
|
||||
Kleisli(req =>
|
||||
@ -46,7 +46,7 @@ object AdminRoutes {
|
||||
)
|
||||
|
||||
private def extractSecret[F[_]](req: Request[F]): Option[String] =
|
||||
req.headers.get(adminHeader).map(_.value)
|
||||
req.headers.get(adminHeader).map(_.head.value)
|
||||
|
||||
private def compareSecret(s1: String)(s2: String): Boolean =
|
||||
s1.length > 0 && s1.length == s2.length &&
|
||||
|
@ -1,6 +1,6 @@
|
||||
package docspell.restserver.routes
|
||||
|
||||
import cats.effect.Effect
|
||||
import cats.effect.Async
|
||||
import cats.implicits._
|
||||
|
||||
import docspell.backend.BackendApp
|
||||
@ -15,7 +15,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object AttachmentMultiRoutes extends MultiIdSupport {
|
||||
|
||||
def apply[F[_]: Effect](
|
||||
def apply[F[_]: Async](
|
||||
backend: BackendApp[F],
|
||||
user: AuthToken
|
||||
): HttpRoutes[F] = {
|
||||
|
@ -22,8 +22,7 @@ import org.http4s.headers._
|
||||
|
||||
object AttachmentRoutes {
|
||||
|
||||
def apply[F[_]: Effect: ContextShift](
|
||||
blocker: Blocker,
|
||||
def apply[F[_]: Async](
|
||||
backend: BackendApp[F],
|
||||
user: AuthToken
|
||||
): HttpRoutes[F] = {
|
||||
@ -51,7 +50,7 @@ object AttachmentRoutes {
|
||||
case req @ GET -> Root / Ident(id) =>
|
||||
for {
|
||||
fileData <- backend.itemSearch.findAttachment(id, user.account.collective)
|
||||
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
|
||||
inm = req.headers.get[`If-None-Match`].flatMap(_.tags)
|
||||
matches = BinaryUtil.matchETag(fileData.map(_.meta), inm)
|
||||
resp <-
|
||||
fileData
|
||||
@ -74,7 +73,7 @@ object AttachmentRoutes {
|
||||
case req @ GET -> Root / Ident(id) / "original" =>
|
||||
for {
|
||||
fileData <- backend.itemSearch.findAttachmentSource(id, user.account.collective)
|
||||
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
|
||||
inm = req.headers.get[`If-None-Match`].flatMap(_.tags)
|
||||
matches = BinaryUtil.matchETag(fileData.map(_.meta), inm)
|
||||
resp <-
|
||||
fileData
|
||||
@ -99,7 +98,7 @@ object AttachmentRoutes {
|
||||
for {
|
||||
fileData <-
|
||||
backend.itemSearch.findAttachmentArchive(id, user.account.collective)
|
||||
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
|
||||
inm = req.headers.get[`If-None-Match`].flatMap(_.tags)
|
||||
matches = BinaryUtil.matchETag(fileData.map(_.meta), inm)
|
||||
resp <-
|
||||
fileData
|
||||
@ -116,7 +115,7 @@ object AttachmentRoutes {
|
||||
for {
|
||||
fileData <-
|
||||
backend.itemSearch.findAttachmentPreview(id, user.account.collective)
|
||||
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
|
||||
inm = req.headers.get[`If-None-Match`].flatMap(_.tags)
|
||||
matches = BinaryUtil.matchETag(fileData.map(_.meta), inm)
|
||||
fallback = flag.getOrElse(false)
|
||||
resp <-
|
||||
@ -126,7 +125,7 @@ object AttachmentRoutes {
|
||||
else makeByteResp(data)
|
||||
}
|
||||
.getOrElse(
|
||||
if (fallback) BinaryUtil.noPreview(blocker, req.some).getOrElseF(notFound)
|
||||
if (fallback) BinaryUtil.noPreview(req.some).getOrElseF(notFound)
|
||||
else notFound
|
||||
)
|
||||
} yield resp
|
||||
@ -158,7 +157,7 @@ object AttachmentRoutes {
|
||||
// it redirects currently to viewerjs
|
||||
val attachUrl = s"/api/v1/sec/attachment/${id.id}"
|
||||
val path = s"/app/assets${Webjars.viewerjs}/ViewerJS/index.html#$attachUrl"
|
||||
SeeOther(Location(Uri(path = path)))
|
||||
SeeOther(Location(Uri(path = Uri.Path.unsafeFromString(path))))
|
||||
|
||||
case GET -> Root / Ident(id) / "meta" =>
|
||||
for {
|
||||
|
@ -14,7 +14,7 @@ import org.http4s.server._
|
||||
|
||||
object Authenticate {
|
||||
|
||||
def authenticateRequest[F[_]: Effect](
|
||||
def authenticateRequest[F[_]: Async](
|
||||
auth: (String, Option[String]) => F[Login.Result]
|
||||
)(req: Request[F]): F[Login.Result] =
|
||||
CookieData.authenticator(req) match {
|
||||
@ -30,7 +30,7 @@ object Authenticate {
|
||||
}
|
||||
}
|
||||
|
||||
def of[F[_]: Effect](S: Login[F], cfg: Login.Config)(
|
||||
def of[F[_]: Async](S: Login[F], cfg: Login.Config)(
|
||||
pf: PartialFunction[AuthedRequest[F, AuthToken], F[Response[F]]]
|
||||
): HttpRoutes[F] = {
|
||||
val dsl: Http4sDsl[F] = new Http4sDsl[F] {}
|
||||
@ -47,7 +47,7 @@ object Authenticate {
|
||||
middleware(AuthedRoutes.of(pf))
|
||||
}
|
||||
|
||||
def apply[F[_]: Effect](S: Login[F], cfg: Login.Config)(
|
||||
def apply[F[_]: Async](S: Login[F], cfg: Login.Config)(
|
||||
f: AuthToken => HttpRoutes[F]
|
||||
): HttpRoutes[F] = {
|
||||
val dsl: Http4sDsl[F] = new Http4sDsl[F] {}
|
||||
@ -64,7 +64,7 @@ object Authenticate {
|
||||
middleware(AuthedRoutes(authReq => f(authReq.context).run(authReq.req)))
|
||||
}
|
||||
|
||||
private def getUser[F[_]: Effect](
|
||||
private def getUser[F[_]: Async](
|
||||
auth: (String, Option[String]) => F[Login.Result]
|
||||
): Kleisli[F, Request[F], Either[String, AuthToken]] =
|
||||
Kleisli(r => authenticateRequest(auth)(r).map(_.toEither))
|
||||
|
@ -14,7 +14,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object CalEventCheckRoutes {
|
||||
|
||||
def apply[F[_]: Effect](): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -16,7 +16,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object CheckFileRoutes {
|
||||
|
||||
def secured[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def secured[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
|
||||
import dsl._
|
||||
|
||||
@ -30,7 +30,7 @@ object CheckFileRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
def open[F[_]: Effect](backend: BackendApp[F]): HttpRoutes[F] = {
|
||||
def open[F[_]: Async](backend: BackendApp[F]): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -16,7 +16,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object ClientSettingsRoutes {
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -19,7 +19,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object CollectiveRoutes {
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -23,7 +23,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object CustomFieldRoutes {
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -18,7 +18,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object EquipmentRoutes {
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -20,7 +20,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object FolderRoutes {
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -15,7 +15,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object FullTextIndexRoutes {
|
||||
|
||||
def secured[F[_]: Effect](
|
||||
def secured[F[_]: Async](
|
||||
cfg: Config,
|
||||
backend: BackendApp[F],
|
||||
user: AuthToken
|
||||
@ -33,7 +33,7 @@ object FullTextIndexRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
def admin[F[_]: Effect](cfg: Config, backend: BackendApp[F]): HttpRoutes[F] =
|
||||
def admin[F[_]: Async](cfg: Config, backend: BackendApp[F]): HttpRoutes[F] =
|
||||
if (!cfg.fullTextSearch.enabled) notFound[F]
|
||||
else {
|
||||
val dsl = Http4sDsl[F]
|
||||
@ -47,6 +47,6 @@ object FullTextIndexRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
private def notFound[F[_]: Effect]: HttpRoutes[F] =
|
||||
private def notFound[F[_]: Async]: HttpRoutes[F] =
|
||||
Responses.notFoundRoute[F]
|
||||
}
|
||||
|
@ -16,13 +16,13 @@ import org.http4s.circe.CirceEntityEncoder._
|
||||
import org.http4s.dsl.Http4sDsl
|
||||
import org.http4s.headers.{Authorization, `WWW-Authenticate`}
|
||||
import org.http4s.multipart.Multipart
|
||||
import org.http4s.util.CaseInsensitiveString
|
||||
import org.log4s.getLogger
|
||||
import org.typelevel.ci.CIString
|
||||
|
||||
object IntegrationEndpointRoutes {
|
||||
private[this] val logger = getLogger
|
||||
|
||||
def open[F[_]: Effect](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
|
||||
def open[F[_]: Async](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
@ -61,12 +61,12 @@ object IntegrationEndpointRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
def checkEnabled[F[_]: Effect](
|
||||
def checkEnabled[F[_]: Async](
|
||||
cfg: Config.IntegrationEndpoint
|
||||
): EitherT[F, Response[F], Unit] =
|
||||
EitherT.cond[F](cfg.enabled, (), Response.notFound[F])
|
||||
|
||||
def authRequest[F[_]: Effect](
|
||||
def authRequest[F[_]: Async](
|
||||
req: Request[F],
|
||||
cfg: Config.IntegrationEndpoint
|
||||
): EitherT[F, Response[F], Unit] = {
|
||||
@ -77,7 +77,7 @@ object IntegrationEndpointRoutes {
|
||||
service.run(req).toLeft(())
|
||||
}
|
||||
|
||||
def lookupCollective[F[_]: Effect](
|
||||
def lookupCollective[F[_]: Async](
|
||||
coll: Ident,
|
||||
backend: BackendApp[F]
|
||||
): EitherT[F, Response[F], Unit] =
|
||||
@ -86,7 +86,7 @@ object IntegrationEndpointRoutes {
|
||||
res <- EitherT.cond[F](opt.exists(_.integrationEnabled), (), Response.notFound[F])
|
||||
} yield res
|
||||
|
||||
def uploadFile[F[_]: Effect](
|
||||
def uploadFile[F[_]: Async](
|
||||
coll: Ident,
|
||||
backend: BackendApp[F],
|
||||
cfg: Config,
|
||||
@ -111,48 +111,48 @@ object IntegrationEndpointRoutes {
|
||||
}
|
||||
|
||||
object HeaderAuth {
|
||||
def apply[F[_]: Effect](cfg: Config.IntegrationEndpoint.HttpHeader): HttpRoutes[F] =
|
||||
def apply[F[_]: Async](cfg: Config.IntegrationEndpoint.HttpHeader): HttpRoutes[F] =
|
||||
if (cfg.enabled) checkHeader(cfg)
|
||||
else HttpRoutes.empty[F]
|
||||
|
||||
def checkHeader[F[_]: Effect](
|
||||
def checkHeader[F[_]: Async](
|
||||
cfg: Config.IntegrationEndpoint.HttpHeader
|
||||
): HttpRoutes[F] =
|
||||
HttpRoutes { req =>
|
||||
val h = req.headers.find(_.name == CaseInsensitiveString(cfg.headerName))
|
||||
if (h.exists(_.value == cfg.headerValue)) OptionT.none[F, Response[F]]
|
||||
val h = req.headers.get(CIString(cfg.headerName))
|
||||
if (h.exists(_.head.value == cfg.headerValue)) OptionT.none[F, Response[F]]
|
||||
else OptionT.pure(Responses.forbidden[F])
|
||||
}
|
||||
}
|
||||
|
||||
object SourceIpAuth {
|
||||
def apply[F[_]: Effect](cfg: Config.IntegrationEndpoint.AllowedIps): HttpRoutes[F] =
|
||||
def apply[F[_]: Async](cfg: Config.IntegrationEndpoint.AllowedIps): HttpRoutes[F] =
|
||||
if (cfg.enabled) checkIps(cfg)
|
||||
else HttpRoutes.empty[F]
|
||||
|
||||
def checkIps[F[_]: Effect](
|
||||
def checkIps[F[_]: Async](
|
||||
cfg: Config.IntegrationEndpoint.AllowedIps
|
||||
): HttpRoutes[F] =
|
||||
HttpRoutes { req =>
|
||||
//The `req.from' take the X-Forwarded-For header into account,
|
||||
//which is not desirable here. The `http-header' auth config
|
||||
//can be used to authenticate based on headers.
|
||||
val from = req.remote.flatMap(remote => Option(remote.getAddress))
|
||||
val from = req.remote.map(_.host)
|
||||
if (from.exists(cfg.containsAddress)) OptionT.none[F, Response[F]]
|
||||
else OptionT.pure(Responses.forbidden[F])
|
||||
}
|
||||
}
|
||||
|
||||
object HttpBasicAuth {
|
||||
def apply[F[_]: Effect](cfg: Config.IntegrationEndpoint.HttpBasic): HttpRoutes[F] =
|
||||
def apply[F[_]: Async](cfg: Config.IntegrationEndpoint.HttpBasic): HttpRoutes[F] =
|
||||
if (cfg.enabled) checkHttpBasic(cfg)
|
||||
else HttpRoutes.empty[F]
|
||||
|
||||
def checkHttpBasic[F[_]: Effect](
|
||||
def checkHttpBasic[F[_]: Async](
|
||||
cfg: Config.IntegrationEndpoint.HttpBasic
|
||||
): HttpRoutes[F] =
|
||||
HttpRoutes { req =>
|
||||
req.headers.get(Authorization) match {
|
||||
req.headers.get[Authorization] match {
|
||||
case Some(auth) =>
|
||||
auth.credentials match {
|
||||
case BasicCredentials(user, pass)
|
||||
|
@ -17,7 +17,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object ItemMultiRoutes extends MultiIdSupport {
|
||||
|
||||
def apply[F[_]: Effect](
|
||||
def apply[F[_]: Async](
|
||||
backend: BackendApp[F],
|
||||
user: AuthToken
|
||||
): HttpRoutes[F] = {
|
||||
|
@ -32,9 +32,8 @@ import org.log4s._
|
||||
object ItemRoutes {
|
||||
private[this] val logger = getLogger
|
||||
|
||||
def apply[F[_]: Effect: ContextShift](
|
||||
def apply[F[_]: Async](
|
||||
cfg: Config,
|
||||
blocker: Blocker,
|
||||
backend: BackendApp[F],
|
||||
user: AuthToken
|
||||
): HttpRoutes[F] = {
|
||||
@ -331,7 +330,7 @@ object ItemRoutes {
|
||||
NotFound(BasicResult(false, "Not found"))
|
||||
for {
|
||||
preview <- backend.itemSearch.findItemPreview(id, user.account.collective)
|
||||
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
|
||||
inm = req.headers.get[`If-None-Match`].flatMap(_.tags)
|
||||
matches = BinaryUtil.matchETag(preview.map(_.meta), inm)
|
||||
fallback = flag.getOrElse(false)
|
||||
resp <-
|
||||
@ -341,7 +340,7 @@ object ItemRoutes {
|
||||
else BinaryUtil.makeByteResp(dsl)(data).map(Responses.noCache)
|
||||
}
|
||||
.getOrElse(
|
||||
if (fallback) BinaryUtil.noPreview(blocker, req.some).getOrElseF(notFound)
|
||||
if (fallback) BinaryUtil.noPreview(req.some).getOrElseF(notFound)
|
||||
else notFound
|
||||
)
|
||||
} yield resp
|
||||
|
@ -16,7 +16,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object JobQueueRoutes {
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -17,7 +17,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object LoginRoutes {
|
||||
|
||||
def login[F[_]: Effect](S: Login[F], cfg: Config): HttpRoutes[F] = {
|
||||
def login[F[_]: Async](S: Login[F], cfg: Config): HttpRoutes[F] = {
|
||||
val dsl: Http4sDsl[F] = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
@ -32,7 +32,7 @@ object LoginRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
def session[F[_]: Effect](S: Login[F], cfg: Config, token: AuthToken): HttpRoutes[F] = {
|
||||
def session[F[_]: Async](S: Login[F], cfg: Config, token: AuthToken): HttpRoutes[F] = {
|
||||
val dsl: Http4sDsl[F] = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
@ -56,7 +56,7 @@ object LoginRoutes {
|
||||
private def getBaseUrl[F[_]](cfg: Config, req: Request[F]): LenientUri =
|
||||
ClientRequestInfo.getBaseUrl(cfg, req)
|
||||
|
||||
private def makeResponse[F[_]: Effect](
|
||||
private def makeResponse[F[_]: Async](
|
||||
dsl: Http4sDsl[F],
|
||||
cfg: Config,
|
||||
req: Request[F],
|
||||
|
@ -19,7 +19,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object MailSendRoutes {
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -22,7 +22,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object MailSettingsRoutes {
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -20,7 +20,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object NotifyDueItemsRoutes {
|
||||
|
||||
def apply[F[_]: Effect](
|
||||
def apply[F[_]: Async](
|
||||
cfg: Config,
|
||||
backend: BackendApp[F],
|
||||
user: AuthToken
|
||||
|
@ -18,7 +18,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object OrganizationRoutes {
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -21,7 +21,7 @@ import org.log4s._
|
||||
object PersonRoutes {
|
||||
private[this] val logger = getLogger
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -19,7 +19,7 @@ import org.log4s._
|
||||
object RegisterRoutes {
|
||||
private[this] val logger = getLogger
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -18,7 +18,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object ScanMailboxRoutes {
|
||||
|
||||
def apply[F[_]: Effect](
|
||||
def apply[F[_]: Async](
|
||||
backend: BackendApp[F],
|
||||
user: AuthToken
|
||||
): HttpRoutes[F] = {
|
||||
|
@ -17,7 +17,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object SentMailRoutes {
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -17,7 +17,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object SourceRoutes {
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -17,7 +17,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object TagRoutes {
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -20,7 +20,7 @@ import org.log4s._
|
||||
object UploadRoutes {
|
||||
private[this] val logger = getLogger
|
||||
|
||||
def secured[F[_]: Effect](
|
||||
def secured[F[_]: Async](
|
||||
backend: BackendApp[F],
|
||||
cfg: Config,
|
||||
user: AuthToken
|
||||
@ -39,7 +39,7 @@ object UploadRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
def open[F[_]: Effect](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
|
||||
def open[F[_]: Async](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
|
||||
import dsl._
|
||||
|
||||
@ -62,7 +62,7 @@ object UploadRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
private def submitFiles[F[_]: Effect](
|
||||
private def submitFiles[F[_]: Async](
|
||||
backend: BackendApp[F],
|
||||
cfg: Config,
|
||||
accOrSrc: Either[Ident, AccountId]
|
||||
|
@ -17,7 +17,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object UserRoutes {
|
||||
|
||||
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
@ -63,7 +63,7 @@ object UserRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
def admin[F[_]: Effect](backend: BackendApp[F]): HttpRoutes[F] = {
|
||||
def admin[F[_]: Async](backend: BackendApp[F]): HttpRoutes[F] = {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
|
@ -30,14 +30,12 @@ object TemplateRoutes {
|
||||
def serviceWorker: HttpRoutes[F]
|
||||
}
|
||||
|
||||
def apply[F[_]: Effect](blocker: Blocker, cfg: Config)(implicit
|
||||
C: ContextShift[F]
|
||||
): InnerRoutes[F] = {
|
||||
def apply[F[_]: Async](cfg: Config): InnerRoutes[F] = {
|
||||
val indexTemplate = memo(
|
||||
loadResource("/index.html").flatMap(loadTemplate(_, blocker))
|
||||
loadResource("/index.html").flatMap(loadTemplate(_))
|
||||
)
|
||||
val docTemplate = memo(loadResource("/doc.html").flatMap(loadTemplate(_, blocker)))
|
||||
val swTemplate = memo(loadResource("/sw.js").flatMap(loadTemplate(_, blocker)))
|
||||
val docTemplate = memo(loadResource("/doc.html").flatMap(loadTemplate(_)))
|
||||
val swTemplate = memo(loadResource("/sw.js").flatMap(loadTemplate(_)))
|
||||
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
@ -84,12 +82,10 @@ object TemplateRoutes {
|
||||
r.pure[F]
|
||||
}
|
||||
|
||||
def loadUrl[F[_]: Sync](url: URL, blocker: Blocker)(implicit
|
||||
C: ContextShift[F]
|
||||
): F[String] =
|
||||
def loadUrl[F[_]: Sync](url: URL): F[String] =
|
||||
Stream
|
||||
.bracket(Sync[F].delay(url.openStream))(in => Sync[F].delay(in.close()))
|
||||
.flatMap(in => fs2.io.readInputStream(in.pure[F], 64 * 1024, blocker, false))
|
||||
.flatMap(in => fs2.io.readInputStream(in.pure[F], 64 * 1024, false))
|
||||
.through(text.utf8Decode)
|
||||
.compile
|
||||
.fold("")(_ + _)
|
||||
@ -102,10 +98,8 @@ object TemplateRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
def loadTemplate[F[_]: Sync](url: URL, blocker: Blocker)(implicit
|
||||
C: ContextShift[F]
|
||||
): F[Template] =
|
||||
loadUrl[F](url, blocker).flatMap(s => parseTemplate(s)).map { t =>
|
||||
def loadTemplate[F[_]: Sync](url: URL): F[Template] =
|
||||
loadUrl[F](url).flatMap(s => parseTemplate(s)).map { t =>
|
||||
logger.info(s"Compiled template $url")
|
||||
t
|
||||
}
|
||||
|
@ -27,19 +27,18 @@ object WebjarRoutes {
|
||||
".xml"
|
||||
)
|
||||
|
||||
def appRoutes[F[_]: Effect](
|
||||
blocker: Blocker
|
||||
)(implicit CS: ContextShift[F]): HttpRoutes[F] =
|
||||
def appRoutes[F[_]: Async]: HttpRoutes[F] =
|
||||
Kleisli {
|
||||
case req if req.method == Method.GET =>
|
||||
val p = req.pathInfo
|
||||
if (p.contains("..") || !suffixes.exists(p.endsWith(_)))
|
||||
val p = req.pathInfo.renderString
|
||||
val last = req.pathInfo.segments.lastOption.map(_.encoded).getOrElse("")
|
||||
val containsColon = req.pathInfo.segments.exists(_.encoded.contains(".."))
|
||||
if (containsColon || !suffixes.exists(last.endsWith(_)))
|
||||
OptionT.pure(Response.notFound[F])
|
||||
else
|
||||
StaticFile
|
||||
.fromResource(
|
||||
s"/META-INF/resources/webjars$p",
|
||||
blocker,
|
||||
Some(req),
|
||||
EnvMode.current.isProd
|
||||
)
|
||||
|
Reference in New Issue
Block a user