mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-03-28 09:45:07 +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
|
collective: Ident
|
||||||
): F[Option[AttachmentPreviewData[F]]]
|
): F[Option[AttachmentPreviewData[F]]]
|
||||||
|
|
||||||
|
def findItemPreview(item: Ident, collective: Ident): F[Option[AttachmentPreviewData[F]]]
|
||||||
|
|
||||||
def findAttachmentMeta(id: Ident, collective: Ident): F[Option[RAttachmentMeta]]
|
def findAttachmentMeta(id: Ident, collective: Ident): F[Option[RAttachmentMeta]]
|
||||||
|
|
||||||
def findByFileCollective(checksum: String, collective: Ident): F[Vector[RItem]]
|
def findByFileCollective(checksum: String, collective: Ident): F[Vector[RItem]]
|
||||||
@ -188,6 +190,26 @@ object OItemSearch {
|
|||||||
(None: Option[AttachmentPreviewData[F]]).pure[F]
|
(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(
|
def findAttachmentArchive(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
collective: Ident
|
collective: Ident
|
||||||
|
@ -1847,6 +1847,47 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/ItemProposals"
|
$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:
|
/sec/item/{itemId}/reprocess:
|
||||||
post:
|
post:
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package docspell.restserver.http4s
|
package docspell.restserver.http4s
|
||||||
|
|
||||||
|
import cats.data.NonEmptyList
|
||||||
import fs2.text.utf8Encode
|
import fs2.text.utf8Encode
|
||||||
import fs2.{Pure, Stream}
|
import fs2.{Pure, Stream}
|
||||||
|
|
||||||
@ -27,4 +28,12 @@ object Responses {
|
|||||||
|
|
||||||
def unauthorized[F[_]]: Response[F] =
|
def unauthorized[F[_]]: Response[F] =
|
||||||
pureUnauthorized.copy(body = pureUnauthorized.body.covary[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.restapi.model._
|
||||||
import docspell.restserver.Config
|
import docspell.restserver.Config
|
||||||
import docspell.restserver.conv.Conversions
|
import docspell.restserver.conv.Conversions
|
||||||
|
import docspell.restserver.http4s.BinaryUtil
|
||||||
|
import docspell.restserver.http4s.Responses
|
||||||
|
|
||||||
import org.http4s.HttpRoutes
|
import org.http4s.HttpRoutes
|
||||||
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._
|
||||||
import org.log4s._
|
import org.log4s._
|
||||||
|
|
||||||
object ItemRoutes {
|
object ItemRoutes {
|
||||||
@ -315,6 +318,29 @@ object ItemRoutes {
|
|||||||
resp <- Ok(Conversions.basicResult(res, "Attachment moved."))
|
resp <- Ok(Conversions.basicResult(res, "Attachment moved."))
|
||||||
} yield resp
|
} 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" =>
|
case req @ POST -> Root / Ident(id) / "reprocess" =>
|
||||||
for {
|
for {
|
||||||
data <- req.as[IdList]
|
data <- req.as[IdList]
|
||||||
|
@ -72,6 +72,31 @@ object RAttachmentPreview {
|
|||||||
.to[Vector]
|
.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(
|
def findByItemWithMeta(
|
||||||
id: Ident
|
id: Ident
|
||||||
): ConnectionIO[Vector[(RAttachmentPreview, FileMeta)]] = {
|
): ConnectionIO[Vector[(RAttachmentPreview, FileMeta)]] = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user