mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-03-25 16:45:05 +00:00
Add a route to get the item preview
This is the first available preview of an attachment wrt position. If all attachments have a preview image, the preview of the first attachment is returned.
This commit is contained in:
parent
8cc89fd3b7
commit
757ad31165
@ -41,6 +41,8 @@ trait OItemSearch[F[_]] {
|
||||
collective: Ident
|
||||
): F[Option[AttachmentPreviewData[F]]]
|
||||
|
||||
def findItemPreview(item: Ident, collective: Ident): F[Option[AttachmentPreviewData[F]]]
|
||||
|
||||
def findAttachmentMeta(id: Ident, collective: Ident): F[Option[RAttachmentMeta]]
|
||||
|
||||
def findByFileCollective(checksum: String, collective: Ident): F[Vector[RItem]]
|
||||
@ -188,6 +190,26 @@ object OItemSearch {
|
||||
(None: Option[AttachmentPreviewData[F]]).pure[F]
|
||||
})
|
||||
|
||||
def findItemPreview(
|
||||
item: Ident,
|
||||
collective: Ident
|
||||
): F[Option[AttachmentPreviewData[F]]] =
|
||||
store
|
||||
.transact(RAttachmentPreview.findByItemAndCollective(item, collective))
|
||||
.flatMap({
|
||||
case Some(ra) =>
|
||||
makeBinaryData(ra.fileId) { m =>
|
||||
AttachmentPreviewData[F](
|
||||
ra,
|
||||
m,
|
||||
store.bitpeace.fetchData2(RangeDef.all)(Stream.emit(m))
|
||||
)
|
||||
}
|
||||
|
||||
case None =>
|
||||
(None: Option[AttachmentPreviewData[F]]).pure[F]
|
||||
})
|
||||
|
||||
def findAttachmentArchive(
|
||||
id: Ident,
|
||||
collective: Ident
|
||||
|
@ -1847,6 +1847,47 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ItemProposals"
|
||||
/sec/item/{id}/preview:
|
||||
head:
|
||||
tags: [ Attachment ]
|
||||
summary: Get a preview image of an attachment file.
|
||||
description: |
|
||||
Checks if an image file showing a preview of the item is
|
||||
available. If not available, a 404 is returned. The preview is
|
||||
currently the an image of the first page of the first
|
||||
attachment.
|
||||
security:
|
||||
- authTokenHeader: []
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/id"
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
404:
|
||||
description: NotFound
|
||||
get:
|
||||
tags: [ Attachment ]
|
||||
summary: Get a preview image of an attachment file.
|
||||
description: |
|
||||
Gets a image file showing a preview of the item. Usually it is
|
||||
a small image of the first page of the first attachment. If
|
||||
not available, a 404 is returned. However, if the query
|
||||
parameter `withFallback` is `true`, a fallback preview image
|
||||
is returned. You can also use the `HEAD` method to check for
|
||||
existence.
|
||||
security:
|
||||
- authTokenHeader: []
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/id"
|
||||
- $ref: "#/components/parameters/withFallback"
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
|
||||
/sec/item/{itemId}/reprocess:
|
||||
post:
|
||||
|
@ -1,5 +1,6 @@
|
||||
package docspell.restserver.http4s
|
||||
|
||||
import cats.data.NonEmptyList
|
||||
import fs2.text.utf8Encode
|
||||
import fs2.{Pure, Stream}
|
||||
|
||||
@ -27,4 +28,12 @@ object Responses {
|
||||
|
||||
def unauthorized[F[_]]: Response[F] =
|
||||
pureUnauthorized.copy(body = pureUnauthorized.body.covary[F])
|
||||
|
||||
def noCache[F[_]](r: Response[F]): Response[F] =
|
||||
r.withHeaders(
|
||||
`Cache-Control`(
|
||||
NonEmptyList.of(CacheDirective.`no-cache`(), CacheDirective.`private`())
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
@ -13,11 +13,14 @@ import docspell.common.{Ident, ItemState}
|
||||
import docspell.restapi.model._
|
||||
import docspell.restserver.Config
|
||||
import docspell.restserver.conv.Conversions
|
||||
import docspell.restserver.http4s.BinaryUtil
|
||||
import docspell.restserver.http4s.Responses
|
||||
|
||||
import org.http4s.HttpRoutes
|
||||
import org.http4s.circe.CirceEntityDecoder._
|
||||
import org.http4s.circe.CirceEntityEncoder._
|
||||
import org.http4s.dsl.Http4sDsl
|
||||
import org.http4s.headers._
|
||||
import org.log4s._
|
||||
|
||||
object ItemRoutes {
|
||||
@ -315,6 +318,29 @@ object ItemRoutes {
|
||||
resp <- Ok(Conversions.basicResult(res, "Attachment moved."))
|
||||
} yield resp
|
||||
|
||||
case req @ GET -> Root / Ident(id) / "preview" =>
|
||||
for {
|
||||
preview <- backend.itemSearch.findItemPreview(id, user.account.collective)
|
||||
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
|
||||
matches = BinaryUtil.matchETag(preview.map(_.meta), inm)
|
||||
resp <-
|
||||
preview
|
||||
.map { data =>
|
||||
if (matches) BinaryUtil.withResponseHeaders(dsl, NotModified())(data)
|
||||
else BinaryUtil.makeByteResp(dsl)(data).map(Responses.noCache)
|
||||
}
|
||||
.getOrElse(NotFound(BasicResult(false, "Not found")))
|
||||
} yield resp
|
||||
|
||||
case HEAD -> Root / Ident(id) / "preview" =>
|
||||
for {
|
||||
preview <- backend.itemSearch.findItemPreview(id, user.account.collective)
|
||||
resp <-
|
||||
preview
|
||||
.map(data => BinaryUtil.withResponseHeaders(dsl, Ok())(data))
|
||||
.getOrElse(NotFound(BasicResult(false, "Not found")))
|
||||
} yield resp
|
||||
|
||||
case req @ POST -> Root / Ident(id) / "reprocess" =>
|
||||
for {
|
||||
data <- req.as[IdList]
|
||||
|
@ -72,6 +72,31 @@ object RAttachmentPreview {
|
||||
.to[Vector]
|
||||
}
|
||||
|
||||
def findByItemAndCollective(
|
||||
itemId: Ident,
|
||||
coll: Ident
|
||||
): ConnectionIO[Option[RAttachmentPreview]] = {
|
||||
val sId = Columns.id.prefix("s")
|
||||
val aId = RAttachment.Columns.id.prefix("a")
|
||||
val aItem = RAttachment.Columns.itemId.prefix("a")
|
||||
val aPos = RAttachment.Columns.position.prefix("a")
|
||||
val iId = RItem.Columns.id.prefix("i")
|
||||
val iColl = RItem.Columns.cid.prefix("i")
|
||||
|
||||
val from =
|
||||
table ++ fr"s INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ sId.is(aId) ++
|
||||
fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ iId.is(aItem)
|
||||
|
||||
selectSimple(
|
||||
all.map(_.prefix("s")) ++ List(aPos),
|
||||
from,
|
||||
and(aItem.is(itemId), iColl.is(coll))
|
||||
)
|
||||
.query[(RAttachmentPreview, Int)]
|
||||
.to[Vector]
|
||||
.map(_.sortBy(_._2).headOption.map(_._1))
|
||||
}
|
||||
|
||||
def findByItemWithMeta(
|
||||
id: Ident
|
||||
): ConnectionIO[Vector[(RAttachmentPreview, FileMeta)]] = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user