mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-21 18:08:25 +00:00
Add routes to retrive the archive of an attachment
This commit is contained in:
@ -8,17 +8,10 @@ import doobie._
|
|||||||
import doobie.implicits._
|
import doobie.implicits._
|
||||||
import docspell.store.{AddResult, Store}
|
import docspell.store.{AddResult, Store}
|
||||||
import docspell.store.queries.{QAttachment, QItem}
|
import docspell.store.queries.{QAttachment, QItem}
|
||||||
import OItem.{AttachmentData, AttachmentSourceData, ItemData, ListItem, Query}
|
import OItem.{AttachmentArchiveData, AttachmentData, AttachmentSourceData, ItemData, ListItem, Query}
|
||||||
import bitpeace.{FileMeta, RangeDef}
|
import bitpeace.{FileMeta, RangeDef}
|
||||||
import docspell.common.{Direction, Ident, ItemState, MetaProposalList, Timestamp}
|
import docspell.common.{Direction, Ident, ItemState, MetaProposalList, Timestamp}
|
||||||
import docspell.store.records.{
|
import docspell.store.records._
|
||||||
RAttachment,
|
|
||||||
RAttachmentMeta,
|
|
||||||
RAttachmentSource,
|
|
||||||
RItem,
|
|
||||||
RSource,
|
|
||||||
RTagItem
|
|
||||||
}
|
|
||||||
|
|
||||||
trait OItem[F[_]] {
|
trait OItem[F[_]] {
|
||||||
|
|
||||||
@ -30,6 +23,8 @@ trait OItem[F[_]] {
|
|||||||
|
|
||||||
def findAttachmentSource(id: Ident, collective: Ident): F[Option[AttachmentSourceData[F]]]
|
def findAttachmentSource(id: Ident, collective: Ident): F[Option[AttachmentSourceData[F]]]
|
||||||
|
|
||||||
|
def findAttachmentArchive(id: Ident, collective: Ident): F[Option[AttachmentArchiveData[F]]]
|
||||||
|
|
||||||
def setTags(item: Ident, tagIds: List[Ident], collective: Ident): F[AddResult]
|
def setTags(item: Ident, tagIds: List[Ident], collective: Ident): F[AddResult]
|
||||||
|
|
||||||
def setDirection(item: Ident, direction: Direction, collective: Ident): F[AddResult]
|
def setDirection(item: Ident, direction: Direction, collective: Ident): F[AddResult]
|
||||||
@ -96,6 +91,15 @@ object OItem {
|
|||||||
val fileId = rs.fileId
|
val fileId = rs.fileId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case class AttachmentArchiveData[F[_]](
|
||||||
|
rs: RAttachmentArchive,
|
||||||
|
meta: FileMeta,
|
||||||
|
data: Stream[F, Byte]
|
||||||
|
) extends BinaryData[F] {
|
||||||
|
val name = rs.name
|
||||||
|
val fileId = rs.fileId
|
||||||
|
}
|
||||||
|
|
||||||
def apply[F[_]: Effect](store: Store[F]): Resource[F, OItem[F]] =
|
def apply[F[_]: Effect](store: Store[F]): Resource[F, OItem[F]] =
|
||||||
Resource.pure[F, OItem[F]](new OItem[F] {
|
Resource.pure[F, OItem[F]](new OItem[F] {
|
||||||
|
|
||||||
@ -139,6 +143,23 @@ object OItem {
|
|||||||
(None: Option[AttachmentSourceData[F]]).pure[F]
|
(None: Option[AttachmentSourceData[F]]).pure[F]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def findAttachmentArchive(id: Ident, collective: Ident): F[Option[AttachmentArchiveData[F]]] =
|
||||||
|
store
|
||||||
|
.transact(RAttachmentArchive.findByIdAndCollective(id, collective))
|
||||||
|
.flatMap({
|
||||||
|
case Some(ra) =>
|
||||||
|
makeBinaryData(ra.fileId) { m =>
|
||||||
|
AttachmentArchiveData[F](
|
||||||
|
ra,
|
||||||
|
m,
|
||||||
|
store.bitpeace.fetchData2(RangeDef.all)(Stream.emit(m))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
case None =>
|
||||||
|
(None: Option[AttachmentArchiveData[F]]).pure[F]
|
||||||
|
})
|
||||||
|
|
||||||
private def makeBinaryData[A](fileId: Ident)(f: FileMeta => A): F[Option[A]] =
|
private def makeBinaryData[A](fileId: Ident)(f: FileMeta => A): F[Option[A]] =
|
||||||
store.bitpeace
|
store.bitpeace
|
||||||
.get(fileId.id)
|
.get(fileId.id)
|
||||||
|
@ -1265,6 +1265,58 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
format: binary
|
format: binary
|
||||||
|
/sec/attachment/{id}/archive:
|
||||||
|
head:
|
||||||
|
tags: [ Attachment ]
|
||||||
|
summary: Get an attachment archive file.
|
||||||
|
description: |
|
||||||
|
Get information about the archive that contains the attachment
|
||||||
|
with the given id.
|
||||||
|
|
||||||
|
If the attachment was not uploaded as part of an archive, 404
|
||||||
|
is returned.
|
||||||
|
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 archive file.
|
||||||
|
description: |
|
||||||
|
Get the archive file that was originally uploaded that
|
||||||
|
contains the attachment with the given id.
|
||||||
|
|
||||||
|
If the attachment was not uploaded as part of an archive, a
|
||||||
|
404 is returned.
|
||||||
|
security:
|
||||||
|
- authTokenHeader: []
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/components/parameters/id"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/octet-stream:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: binary
|
||||||
/sec/attachment/{id}/meta:
|
/sec/attachment/{id}/meta:
|
||||||
get:
|
get:
|
||||||
tags: [ Attachment ]
|
tags: [ Attachment ]
|
||||||
|
@ -83,6 +83,28 @@ object AttachmentRoutes {
|
|||||||
.getOrElse(NotFound(BasicResult(false, "Not found")))
|
.getOrElse(NotFound(BasicResult(false, "Not found")))
|
||||||
} yield resp
|
} yield resp
|
||||||
|
|
||||||
|
case HEAD -> Root / Ident(id) / "archive" =>
|
||||||
|
for {
|
||||||
|
fileData <- backend.item.findAttachmentArchive(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) / "archive" =>
|
||||||
|
for {
|
||||||
|
fileData <- backend.item.findAttachmentArchive(id, user.account.collective)
|
||||||
|
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
|
||||||
|
matches = matchETag(fileData.map(_.meta), inm)
|
||||||
|
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" =>
|
case GET -> Root / Ident(id) / "view" =>
|
||||||
// this route exists to provide a stable url
|
// this route exists to provide a stable url
|
||||||
// it redirects currently to viewerjs
|
// it redirects currently to viewerjs
|
||||||
|
Reference in New Issue
Block a user