mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-03-28 17:55:06 +00:00
Add attachment preview url based on ViewerJS
The viewerJS library can display PDF files easily using pdfjs. Another attachment route redirects to the viewerjs application to display the current attachment. The attachment responses have been improved in that now the response headers are added to all responses. Additional a HEAD route has been added to support the viewerJS application.
This commit is contained in:
parent
e1826f39ac
commit
8908ad2561
@ -1168,6 +1168,32 @@ paths:
|
||||
$ref: "#/components/schemas/ItemProposals"
|
||||
|
||||
/sec/attachment/{id}:
|
||||
head:
|
||||
tags: [ Attachment ]
|
||||
summary: Get an attachment file.
|
||||
description: |
|
||||
Get the binary file belonging to the attachment with the given id.
|
||||
security:
|
||||
- authTokenHeader: []
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/id"
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
headers:
|
||||
Content-Type:
|
||||
schema:
|
||||
type: string
|
||||
Content-Length:
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
ETag:
|
||||
schema:
|
||||
type: string
|
||||
Content-Disposition:
|
||||
schema:
|
||||
type: string
|
||||
get:
|
||||
tags: [ Attachment ]
|
||||
summary: Get an attachment file.
|
||||
@ -1203,6 +1229,27 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/AttachmentMeta"
|
||||
/sec/attachment/{id}/view:
|
||||
get:
|
||||
tags: [ Attachment ]
|
||||
summary: A preview of the attachment
|
||||
description: |
|
||||
This provides a preview of the attachment. It currently uses a
|
||||
third-party javascript library (viewerjs) to display the
|
||||
preview. This works by redirecting to the viewerjs url with
|
||||
the attachment url as parameter. Note that the resulting url
|
||||
that is redirected to is not stable. It may change from
|
||||
version to version. This route, however, is meant to provide a
|
||||
stable url for the preview.
|
||||
security:
|
||||
- authTokenHeader: []
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/id"
|
||||
responses:
|
||||
303:
|
||||
description: See Other
|
||||
200:
|
||||
description: Ok
|
||||
/sec/queue/state:
|
||||
get:
|
||||
tags: [ Job Queue ]
|
||||
|
@ -3,17 +3,18 @@ package docspell.restserver.routes
|
||||
import cats.data.NonEmptyList
|
||||
import cats.effect._
|
||||
import cats.implicits._
|
||||
import org.http4s._
|
||||
import org.http4s.dsl.Http4sDsl
|
||||
import org.http4s.headers._
|
||||
import org.http4s.headers.ETag.EntityTag
|
||||
import org.http4s.circe.CirceEntityEncoder._
|
||||
import docspell.backend.BackendApp
|
||||
import docspell.backend.auth.AuthToken
|
||||
import docspell.backend.ops.OItem
|
||||
import docspell.common.Ident
|
||||
import org.http4s.{Header, HttpRoutes, MediaType, Response}
|
||||
import org.http4s.dsl.Http4sDsl
|
||||
import org.http4s.headers._
|
||||
import org.http4s.circe.CirceEntityEncoder._
|
||||
import docspell.restapi.model._
|
||||
import docspell.restserver.conv.Conversions
|
||||
import org.http4s.headers.ETag.EntityTag
|
||||
import docspell.restserver.webapp.Webjars
|
||||
|
||||
object AttachmentRoutes {
|
||||
|
||||
@ -21,28 +22,52 @@ object AttachmentRoutes {
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
def makeByteResp(data: OItem.AttachmentData[F]): F[Response[F]] = {
|
||||
def withResponseHeaders(resp: F[Response[F]])(data: OItem.AttachmentData[F]): F[Response[F]] = {
|
||||
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.ra.name.getOrElse("")))
|
||||
Ok(data.data.take(data.meta.length)).map(r =>
|
||||
r.withContentType(`Content-Type`(mt)).withHeaders(cntLen, eTag, disp)
|
||||
|
||||
resp.map(r =>
|
||||
if (r.status == NotModified) r.withHeaders(ctype, eTag, disp)
|
||||
else r.withHeaders(ctype, cntLen, eTag, disp)
|
||||
)
|
||||
}
|
||||
|
||||
def makeByteResp(data: OItem.AttachmentData[F]): F[Response[F]] =
|
||||
withResponseHeaders(Ok(data.data.take(data.meta.length)))(data)
|
||||
|
||||
HttpRoutes.of {
|
||||
case HEAD -> Root / Ident(id) =>
|
||||
for {
|
||||
fileData <- backend.item.findAttachment(id, user.account.collective)
|
||||
resp <- fileData
|
||||
.map(data => withResponseHeaders(Ok())(data))
|
||||
.getOrElse(NotFound(BasicResult(false, "Not found")))
|
||||
} yield resp
|
||||
|
||||
case req @ GET -> Root / Ident(id) =>
|
||||
for {
|
||||
fileData <- backend.item.findAttachment(id, user.account.collective)
|
||||
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
|
||||
matches = matchETag(fileData, inm)
|
||||
resp <- if (matches) NotModified()
|
||||
else
|
||||
fileData.map(makeByteResp).getOrElse(NotFound(BasicResult(false, "Not found")))
|
||||
resp <- fileData
|
||||
.map({ data =>
|
||||
if (matches) withResponseHeaders(NotModified())(data)
|
||||
else makeByteResp(data)
|
||||
})
|
||||
.getOrElse(NotFound(BasicResult(false, "Not found")))
|
||||
} yield resp
|
||||
|
||||
case GET -> Root / Ident(id) / "view" =>
|
||||
// this route exists to provide a stable url
|
||||
// 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)))
|
||||
|
||||
case GET -> Root / Ident(id) / "meta" =>
|
||||
for {
|
||||
rm <- backend.item.findAttachmentMeta(id, user.account.collective)
|
||||
|
@ -26,6 +26,10 @@ object Dependencies {
|
||||
val StanfordNlpVersion = "3.9.2"
|
||||
val TikaVersion = "1.23"
|
||||
val YamuscaVersion = "0.6.1"
|
||||
val SwaggerUIVersion = "3.24.3"
|
||||
val SemanticUIVersion = "2.4.1"
|
||||
val JQueryVersion = "3.4.1"
|
||||
val ViewerJSVersion = "0.5.8"
|
||||
|
||||
val emil = Seq(
|
||||
"com.github.eikek" %% "emil-common" % EmilVersion,
|
||||
@ -149,9 +153,10 @@ object Dependencies {
|
||||
val betterMonadicFor = "com.olegpy" %% "better-monadic-for" % BetterMonadicForVersion
|
||||
|
||||
val webjars = Seq(
|
||||
"swagger-ui" -> "3.24.3",
|
||||
"Semantic-UI" -> "2.4.1",
|
||||
"jquery" -> "3.4.1"
|
||||
).map({case (a, v) => "org.webjars" % a % v })
|
||||
"org.webjars" % "swagger-ui" % SwaggerUIVersion,
|
||||
"org.webjars" % "Semantic-UI"% SemanticUIVersion,
|
||||
"org.webjars" % "jquery" % JQueryVersion,
|
||||
"org.webjars" % "viewerjs" % ViewerJSVersion
|
||||
)
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user