mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 02:18:26 +00:00
Check file integrity
This commit is contained in:
@ -92,7 +92,7 @@ object BackendApp {
|
||||
)
|
||||
notifyImpl <- ONotification(store, notificationMod)
|
||||
bookmarksImpl <- OQueryBookmarks(store)
|
||||
fileRepoImpl <- OFileRepository(queue, joexImpl)
|
||||
fileRepoImpl <- OFileRepository(store, queue, joexImpl)
|
||||
} yield new BackendApp[F] {
|
||||
val pubSub = pubSubT
|
||||
val login = loginImpl
|
||||
|
@ -15,6 +15,26 @@ import docspell.notification.api.PeriodicQueryArgs
|
||||
import docspell.store.records.RJob
|
||||
|
||||
object JobFactory extends MailAddressCodec {
|
||||
def integrityCheck[F[_]: Sync](
|
||||
args: FileIntegrityCheckArgs,
|
||||
submitter: AccountId = DocspellSystem.account
|
||||
): F[RJob] =
|
||||
for {
|
||||
id <- Ident.randomId[F]
|
||||
now <- Timestamp.current[F]
|
||||
job = RJob.newJob(
|
||||
id,
|
||||
FileIntegrityCheckArgs.taskName,
|
||||
submitter.collective,
|
||||
args,
|
||||
s"Check integrity of files",
|
||||
now,
|
||||
submitter.user,
|
||||
Priority.High,
|
||||
Some(FileIntegrityCheckArgs.taskName)
|
||||
)
|
||||
} yield job
|
||||
|
||||
def fileCopy[F[_]: Sync](
|
||||
args: FileCopyTaskArgs,
|
||||
submitter: AccountId = DocspellSystem.account
|
||||
|
@ -6,27 +6,41 @@
|
||||
|
||||
package docspell.backend.ops
|
||||
|
||||
import cats.data.OptionT
|
||||
import cats.effect._
|
||||
import cats.implicits._
|
||||
|
||||
import docspell.backend.JobFactory
|
||||
import docspell.common.FileCopyTaskArgs
|
||||
import docspell.backend.ops.OFileRepository.IntegrityResult
|
||||
import docspell.common._
|
||||
import docspell.store.Store
|
||||
import docspell.store.queue.JobQueue
|
||||
import docspell.store.records.RJob
|
||||
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
trait OFileRepository[F[_]] {
|
||||
|
||||
/** Inserts the job or return None if such a job already is running. */
|
||||
def cloneFileRepository(args: FileCopyTaskArgs, notifyJoex: Boolean): F[Option[RJob]]
|
||||
|
||||
def checkIntegrityAll(part: FileKeyPart, notifyJoex: Boolean): F[Option[RJob]]
|
||||
|
||||
def checkIntegrity(key: FileKey, hash: Option[ByteVector]): F[Option[IntegrityResult]]
|
||||
}
|
||||
|
||||
object OFileRepository {
|
||||
|
||||
case class IntegrityResult(ok: Boolean, key: FileKey)
|
||||
|
||||
def apply[F[_]: Async](
|
||||
store: Store[F],
|
||||
queue: JobQueue[F],
|
||||
joex: OJoex[F]
|
||||
): Resource[F, OFileRepository[F]] =
|
||||
Resource.pure(new OFileRepository[F] {
|
||||
private[this] val logger = docspell.logging.getLogger[F]
|
||||
|
||||
def cloneFileRepository(
|
||||
args: FileCopyTaskArgs,
|
||||
notifyJoex: Boolean
|
||||
@ -36,5 +50,43 @@ object OFileRepository {
|
||||
flag <- queue.insertIfNew(job)
|
||||
_ <- if (notifyJoex) joex.notifyAllNodes else ().pure[F]
|
||||
} yield Option.when(flag)(job)
|
||||
|
||||
def checkIntegrityAll(part: FileKeyPart, notifyJoex: Boolean): F[Option[RJob]] =
|
||||
for {
|
||||
job <- JobFactory.integrityCheck(FileIntegrityCheckArgs(part))
|
||||
flag <- queue.insertIfNew(job)
|
||||
_ <- if (notifyJoex) joex.notifyAllNodes else ().pure[F]
|
||||
} yield Option.when(flag)(job)
|
||||
|
||||
def checkIntegrity(
|
||||
key: FileKey,
|
||||
hash: Option[ByteVector]
|
||||
): F[Option[IntegrityResult]] =
|
||||
(for {
|
||||
_ <- OptionT.liftF(
|
||||
logger.debugWith(s"Checking file $key")(_.data("fileKey", key))
|
||||
)
|
||||
expectedHash <-
|
||||
hash.fold(OptionT(store.fileRepo.findMeta(key)).map(_.checksum))(h =>
|
||||
OptionT.pure[F](h)
|
||||
)
|
||||
|
||||
actualHash <-
|
||||
OptionT.liftF(
|
||||
logger.debugWith(s"Calculating new hash for $key")(
|
||||
_.data("fileKey", key)
|
||||
) *>
|
||||
store.fileRepo
|
||||
.getBytes(key)
|
||||
.through(fs2.hash.sha256)
|
||||
.compile
|
||||
.foldChunks(ByteVector.empty)(_ ++ _.toByteVector)
|
||||
)
|
||||
res = IntegrityResult(expectedHash == actualHash, key)
|
||||
_ <- OptionT.liftF {
|
||||
if (res.ok) logger.debug(s"File hashes match for $key")
|
||||
else logger.warnWith(s"File hashes differ for: $key")(_.data("fileKey", key))
|
||||
}
|
||||
} yield res).value
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user