mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-03-28 17:55:06 +00:00
Move handling binary responses to a shared space
This commit is contained in:
parent
d376ef3ef1
commit
8cc89fd3b7
@ -0,0 +1,54 @@
|
|||||||
|
package docspell.restserver.http4s
|
||||||
|
|
||||||
|
import cats.data.NonEmptyList
|
||||||
|
import cats.effect._
|
||||||
|
import cats.implicits._
|
||||||
|
|
||||||
|
import docspell.backend.ops._
|
||||||
|
|
||||||
|
import bitpeace.FileMeta
|
||||||
|
import org.http4s._
|
||||||
|
import org.http4s.circe.CirceEntityEncoder._
|
||||||
|
import org.http4s.dsl.Http4sDsl
|
||||||
|
import org.http4s.headers.ETag.EntityTag
|
||||||
|
import org.http4s.headers._
|
||||||
|
|
||||||
|
object BinaryUtil {
|
||||||
|
|
||||||
|
def withResponseHeaders[F[_]: Sync](dsl: Http4sDsl[F], resp: F[Response[F]])(
|
||||||
|
data: OItemSearch.BinaryData[F]
|
||||||
|
): 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("")))
|
||||||
|
|
||||||
|
resp.map(r =>
|
||||||
|
if (r.status == NotModified) r.withHeaders(ctype, eTag, disp)
|
||||||
|
else r.withHeaders(ctype, cntLen, eTag, disp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def makeByteResp[F[_]: Sync](
|
||||||
|
dsl: Http4sDsl[F]
|
||||||
|
)(data: OItemSearch.BinaryData[F]): F[Response[F]] = {
|
||||||
|
import dsl._
|
||||||
|
withResponseHeaders(dsl, Ok(data.data.take(data.meta.length)))(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
def matchETag[F[_]](
|
||||||
|
fileData: Option[FileMeta],
|
||||||
|
noneMatch: Option[NonEmptyList[EntityTag]]
|
||||||
|
): Boolean =
|
||||||
|
(fileData, noneMatch) match {
|
||||||
|
case (Some(meta), Some(nm)) =>
|
||||||
|
meta.checksum == nm.head.tag
|
||||||
|
case _ =>
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package docspell.restserver.routes
|
package docspell.restserver.routes
|
||||||
|
|
||||||
import cats.data.NonEmptyList
|
|
||||||
import cats.effect._
|
import cats.effect._
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
|
||||||
@ -10,14 +9,13 @@ import docspell.backend.ops._
|
|||||||
import docspell.common.Ident
|
import docspell.common.Ident
|
||||||
import docspell.restapi.model._
|
import docspell.restapi.model._
|
||||||
import docspell.restserver.conv.Conversions
|
import docspell.restserver.conv.Conversions
|
||||||
|
import docspell.restserver.http4s.BinaryUtil
|
||||||
import docspell.restserver.webapp.Webjars
|
import docspell.restserver.webapp.Webjars
|
||||||
|
|
||||||
import bitpeace.FileMeta
|
|
||||||
import org.http4s._
|
import org.http4s._
|
||||||
import org.http4s.circe.CirceEntityDecoder._
|
import org.http4s.circe.CirceEntityDecoder._
|
||||||
import org.http4s.circe.CirceEntityEncoder._
|
import org.http4s.circe.CirceEntityEncoder._
|
||||||
import org.http4s.dsl.Http4sDsl
|
import org.http4s.dsl.Http4sDsl
|
||||||
import org.http4s.headers.ETag.EntityTag
|
|
||||||
import org.http4s.headers._
|
import org.http4s.headers._
|
||||||
|
|
||||||
object AttachmentRoutes {
|
object AttachmentRoutes {
|
||||||
@ -26,24 +24,13 @@ object AttachmentRoutes {
|
|||||||
val dsl = new Http4sDsl[F] {}
|
val dsl = new Http4sDsl[F] {}
|
||||||
import dsl._
|
import dsl._
|
||||||
|
|
||||||
def withResponseHeaders(
|
def withResponseHeaders(resp: F[Response[F]])(
|
||||||
resp: F[Response[F]]
|
data: OItemSearch.BinaryData[F]
|
||||||
)(data: OItemSearch.BinaryData[F]): F[Response[F]] = {
|
): F[Response[F]] =
|
||||||
val mt = MediaType.unsafeParse(data.meta.mimetype.asString)
|
BinaryUtil.withResponseHeaders[F](dsl, resp)(data)
|
||||||
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("")))
|
|
||||||
|
|
||||||
resp.map(r =>
|
|
||||||
if (r.status == NotModified) r.withHeaders(ctype, eTag, disp)
|
|
||||||
else r.withHeaders(ctype, cntLen, eTag, disp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
def makeByteResp(data: OItemSearch.BinaryData[F]): F[Response[F]] =
|
def makeByteResp(data: OItemSearch.BinaryData[F]): F[Response[F]] =
|
||||||
withResponseHeaders(Ok(data.data.take(data.meta.length)))(data)
|
BinaryUtil.makeByteResp(dsl)(data)
|
||||||
|
|
||||||
HttpRoutes.of {
|
HttpRoutes.of {
|
||||||
case HEAD -> Root / Ident(id) =>
|
case HEAD -> Root / Ident(id) =>
|
||||||
@ -59,7 +46,7 @@ object AttachmentRoutes {
|
|||||||
for {
|
for {
|
||||||
fileData <- backend.itemSearch.findAttachment(id, user.account.collective)
|
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 = matchETag(fileData.map(_.meta), inm)
|
matches = BinaryUtil.matchETag(fileData.map(_.meta), inm)
|
||||||
resp <-
|
resp <-
|
||||||
fileData
|
fileData
|
||||||
.map { data =>
|
.map { data =>
|
||||||
@ -82,7 +69,7 @@ object AttachmentRoutes {
|
|||||||
for {
|
for {
|
||||||
fileData <- backend.itemSearch.findAttachmentSource(id, user.account.collective)
|
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 = matchETag(fileData.map(_.meta), inm)
|
matches = BinaryUtil.matchETag(fileData.map(_.meta), inm)
|
||||||
resp <-
|
resp <-
|
||||||
fileData
|
fileData
|
||||||
.map { data =>
|
.map { data =>
|
||||||
@ -107,7 +94,7 @@ object AttachmentRoutes {
|
|||||||
fileData <-
|
fileData <-
|
||||||
backend.itemSearch.findAttachmentArchive(id, user.account.collective)
|
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 = matchETag(fileData.map(_.meta), inm)
|
matches = BinaryUtil.matchETag(fileData.map(_.meta), inm)
|
||||||
resp <-
|
resp <-
|
||||||
fileData
|
fileData
|
||||||
.map { data =>
|
.map { data =>
|
||||||
@ -122,7 +109,7 @@ object AttachmentRoutes {
|
|||||||
fileData <-
|
fileData <-
|
||||||
backend.itemSearch.findAttachmentPreview(id, user.account.collective)
|
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 = matchETag(fileData.map(_.meta), inm)
|
matches = BinaryUtil.matchETag(fileData.map(_.meta), inm)
|
||||||
resp <-
|
resp <-
|
||||||
fileData
|
fileData
|
||||||
.map { data =>
|
.map { data =>
|
||||||
@ -173,16 +160,4 @@ object AttachmentRoutes {
|
|||||||
} yield resp
|
} yield resp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def matchETag[F[_]](
|
|
||||||
fileData: Option[FileMeta],
|
|
||||||
noneMatch: Option[NonEmptyList[EntityTag]]
|
|
||||||
): Boolean =
|
|
||||||
(fileData, noneMatch) match {
|
|
||||||
case (Some(meta), Some(nm)) =>
|
|
||||||
meta.checksum == nm.head.tag
|
|
||||||
case _ =>
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user