diff --git a/build.sbt b/build.sbt index 2d9057ac..4c9db62f 100644 --- a/build.sbt +++ b/build.sbt @@ -16,7 +16,7 @@ val scalafixSettings = Seq( val sharedSettings = Seq( organization := "com.github.eikek", - scalaVersion := "2.13.10", + scalaVersion := "2.13.12", organizationName := "Eike K. & Contributors", licenses += ("AGPL-3.0-or-later", url( "https://spdx.org/licenses/AGPL-3.0-or-later.html" diff --git a/modules/addonlib/src/main/scala/docspell/addons/AddonArchive.scala b/modules/addonlib/src/main/scala/docspell/addons/AddonArchive.scala index e12d8354..d4a367be 100644 --- a/modules/addonlib/src/main/scala/docspell/addons/AddonArchive.scala +++ b/modules/addonlib/src/main/scala/docspell/addons/AddonArchive.scala @@ -19,7 +19,7 @@ final case class AddonArchive(url: LenientUri, name: String, version: String) { def nameAndVersion: String = s"$name-$version" - def extractTo[F[_]: Async]( + def extractTo[F[_]: Async: Files]( reader: UrlReader[F], directory: Path, withSubdir: Boolean = true, @@ -48,7 +48,7 @@ final case class AddonArchive(url: LenientUri, name: String, version: String) { /** Read meta either from the given directory or extract the url to find the metadata * file to read */ - def readMeta[F[_]: Async]( + def readMeta[F[_]: Async: Files]( urlReader: UrlReader[F], directory: Option[Path] = None ): F[AddonMeta] = @@ -58,7 +58,7 @@ final case class AddonArchive(url: LenientUri, name: String, version: String) { } object AddonArchive { - def read[F[_]: Async]( + def read[F[_]: Async: Files]( url: LenientUri, urlReader: UrlReader[F], extractDir: Option[Path] = None @@ -69,7 +69,7 @@ object AddonArchive { .map(m => addon.copy(name = m.meta.name, version = m.meta.version)) } - def dockerAndFlakeExists[F[_]: Async]( + def dockerAndFlakeExists[F[_]: Async: Files]( archive: Either[Path, Stream[F, Byte]] ): F[(Boolean, Boolean)] = { val files = Files[F] diff --git a/modules/addonlib/src/main/scala/docspell/addons/AddonExecutor.scala b/modules/addonlib/src/main/scala/docspell/addons/AddonExecutor.scala index 79a496ca..e79581d7 100644 --- a/modules/addonlib/src/main/scala/docspell/addons/AddonExecutor.scala +++ b/modules/addonlib/src/main/scala/docspell/addons/AddonExecutor.scala @@ -29,7 +29,7 @@ trait AddonExecutor[F[_]] { object AddonExecutor { - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( cfg: AddonExecutorConfig, urlReader: UrlReader[F] ): AddonExecutor[F] = @@ -104,7 +104,7 @@ object AddonExecutor { } yield result } - def selectRunner[F[_]: Async]( + def selectRunner[F[_]: Async: Files]( cfg: AddonExecutorConfig, meta: AddonMeta, addonDir: Path diff --git a/modules/addonlib/src/main/scala/docspell/addons/AddonMeta.scala b/modules/addonlib/src/main/scala/docspell/addons/AddonMeta.scala index e68917d6..c6ad5fb6 100644 --- a/modules/addonlib/src/main/scala/docspell/addons/AddonMeta.scala +++ b/modules/addonlib/src/main/scala/docspell/addons/AddonMeta.scala @@ -50,7 +50,7 @@ case class AddonMeta( * inspecting the archive to return defaults when the addon isn't declaring it in the * descriptor. */ - def enabledTypes[F[_]: Async]( + def enabledTypes[F[_]: Async: Files]( archive: Either[Path, Stream[F, Byte]] ): F[List[RunnerType]] = for { @@ -207,7 +207,7 @@ object AddonMeta { ) } - def findInZip[F[_]: Async](zipFile: Stream[F, Byte]): F[AddonMeta] = { + def findInZip[F[_]: Async: Files](zipFile: Stream[F, Byte]): F[AddonMeta] = { val logger = docspell.logging.getLogger[F] val fail: F[AddonMeta] = Async[F].raiseError( new FileNotFoundException( diff --git a/modules/addonlib/src/main/scala/docspell/addons/AddonRunner.scala b/modules/addonlib/src/main/scala/docspell/addons/AddonRunner.scala index 75efe4a2..32c40bfe 100644 --- a/modules/addonlib/src/main/scala/docspell/addons/AddonRunner.scala +++ b/modules/addonlib/src/main/scala/docspell/addons/AddonRunner.scala @@ -10,6 +10,7 @@ import cats.Applicative import cats.effect._ import cats.syntax.all._ import fs2.Stream +import fs2.io.file.Files import docspell.addons.runner._ import docspell.common.exec.Env @@ -26,7 +27,9 @@ trait AddonRunner[F[_]] { } object AddonRunner { - def forType[F[_]: Async](cfg: AddonExecutorConfig)(rt: RunnerType) = + def forType[F[_]: Async: Files]( + cfg: AddonExecutorConfig + )(rt: RunnerType): AddonRunner[F] = rt match { case RunnerType.NixFlake => NixFlakeRunner[F](cfg) case RunnerType.Docker => DockerRunner[F](cfg) @@ -38,9 +41,9 @@ object AddonRunner { def pure[F[_]: Applicative](result: AddonResult): AddonRunner[F] = new AddonRunner[F] { - val runnerType = Nil + val runnerType: List[RunnerType] = Nil - def run(logger: Logger[F], env: Env, ctx: Context) = + def run(logger: Logger[F], env: Env, ctx: Context): F[AddonResult] = Applicative[F].pure(result) } @@ -50,9 +53,9 @@ object AddonRunner { case a :: Nil => a case _ => new AddonRunner[F] { - val runnerType = runners.flatMap(_.runnerType).distinct + val runnerType: List[RunnerType] = runners.flatMap(_.runnerType).distinct - def run(logger: Logger[F], env: Env, ctx: Context) = + def run(logger: Logger[F], env: Env, ctx: Context): F[AddonResult] = Stream .emits(runners) .evalTap(r => diff --git a/modules/addonlib/src/main/scala/docspell/addons/runner/NixFlakeRunner.scala b/modules/addonlib/src/main/scala/docspell/addons/runner/NixFlakeRunner.scala index ce12c73c..b255c256 100644 --- a/modules/addonlib/src/main/scala/docspell/addons/runner/NixFlakeRunner.scala +++ b/modules/addonlib/src/main/scala/docspell/addons/runner/NixFlakeRunner.scala @@ -18,7 +18,7 @@ import docspell.common.Duration import docspell.common.exec._ import docspell.logging.Logger -final class NixFlakeRunner[F[_]: Async](cfg: NixFlakeRunner.Config) +final class NixFlakeRunner[F[_]: Async: Files](cfg: NixFlakeRunner.Config) extends AddonRunner[F] { val runnerType = List(RunnerType.NixFlake) @@ -104,7 +104,7 @@ final class NixFlakeRunner[F[_]: Async](cfg: NixFlakeRunner.Config) } object NixFlakeRunner { - def apply[F[_]: Async](cfg: AddonExecutorConfig): NixFlakeRunner[F] = + def apply[F[_]: Async: Files](cfg: AddonExecutorConfig): NixFlakeRunner[F] = new NixFlakeRunner[F](Config(cfg.nixRunner, cfg.nspawn, cfg.runTimeout)) case class Config( diff --git a/modules/addonlib/src/main/scala/docspell/addons/runner/RunnerUtil.scala b/modules/addonlib/src/main/scala/docspell/addons/runner/RunnerUtil.scala index e404b442..7f44dcea 100644 --- a/modules/addonlib/src/main/scala/docspell/addons/runner/RunnerUtil.scala +++ b/modules/addonlib/src/main/scala/docspell/addons/runner/RunnerUtil.scala @@ -45,7 +45,7 @@ private[addons] object RunnerUtil { * expected to be relative to the `ctx.baseDir`. Additional arguments and environment * variables are added as configured in the addon. */ - def runInContainer[F[_]: Async]( + def runInContainer[F[_]: Async: Files]( logger: Logger[F], cfg: AddonExecutorConfig.NSpawn, ctx: Context diff --git a/modules/addonlib/src/main/scala/docspell/addons/runner/TrivialRunner.scala b/modules/addonlib/src/main/scala/docspell/addons/runner/TrivialRunner.scala index 31ac2694..9ec4edbd 100644 --- a/modules/addonlib/src/main/scala/docspell/addons/runner/TrivialRunner.scala +++ b/modules/addonlib/src/main/scala/docspell/addons/runner/TrivialRunner.scala @@ -19,10 +19,12 @@ import docspell.common.Duration import docspell.common.exec.{Args, Env, SysCmd} import docspell.logging.Logger -final class TrivialRunner[F[_]: Async](cfg: TrivialRunner.Config) extends AddonRunner[F] { +final class TrivialRunner[F[_]: Async: Files](cfg: TrivialRunner.Config) + extends AddonRunner[F] { private val sync = Async[F] private val files = Files[F] - implicit val andMonoid: Monoid[Boolean] = Monoid.instance[Boolean](true, _ && _) + implicit val andMonoid: Monoid[Boolean] = + Monoid.instance[Boolean](emptyValue = true, _ && _) private val executeBits = PosixPermissions( OwnerExecute, @@ -34,13 +36,13 @@ final class TrivialRunner[F[_]: Async](cfg: TrivialRunner.Config) extends AddonR OthersRead ) - val runnerType = List(RunnerType.Trivial) + val runnerType: List[RunnerType] = List(RunnerType.Trivial) def run( logger: Logger[F], env: Env, ctx: Context - ) = { + ): F[AddonResult] = { val binaryPath = ctx.meta.runner .flatMap(_.trivial) .map(_.exec) @@ -71,7 +73,7 @@ final class TrivialRunner[F[_]: Async](cfg: TrivialRunner.Config) extends AddonR } object TrivialRunner { - def apply[F[_]: Async](cfg: AddonExecutorConfig): TrivialRunner[F] = + def apply[F[_]: Async: Files](cfg: AddonExecutorConfig): TrivialRunner[F] = new TrivialRunner[F](Config(cfg.nspawn, cfg.runTimeout)) case class Config(nspawn: NSpawn, timeout: Duration) diff --git a/modules/analysis/src/main/scala/docspell/analysis/TextAnalyser.scala b/modules/analysis/src/main/scala/docspell/analysis/TextAnalyser.scala index ca92b377..0ab5e975 100644 --- a/modules/analysis/src/main/scala/docspell/analysis/TextAnalyser.scala +++ b/modules/analysis/src/main/scala/docspell/analysis/TextAnalyser.scala @@ -9,6 +9,7 @@ package docspell.analysis import cats.Applicative import cats.effect._ import cats.implicits._ +import fs2.io.file.Files import docspell.analysis.classifier.{StanfordTextClassifier, TextClassifier} import docspell.analysis.contact.Contact @@ -36,7 +37,7 @@ object TextAnalyser { labels ++ dates.map(dl => dl.label.copy(label = dl.date.toString)) } - def create[F[_]: Async](cfg: TextAnalysisConfig): Resource[F, TextAnalyser[F]] = + def create[F[_]: Async: Files](cfg: TextAnalysisConfig): Resource[F, TextAnalyser[F]] = Resource .eval(Nlp(cfg.nlpConfig)) .map(stanfordNer => @@ -83,7 +84,7 @@ object TextAnalyser { /** Provides the nlp pipeline based on the configuration. */ private object Nlp { - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( cfg: TextAnalysisConfig.NlpConfig ): F[Input[F] => F[Vector[NerLabel]]] = { val log = docspell.logging.getLogger[F] diff --git a/modules/analysis/src/main/scala/docspell/analysis/classifier/StanfordTextClassifier.scala b/modules/analysis/src/main/scala/docspell/analysis/classifier/StanfordTextClassifier.scala index 9542d3bb..c9e1e064 100644 --- a/modules/analysis/src/main/scala/docspell/analysis/classifier/StanfordTextClassifier.scala +++ b/modules/analysis/src/main/scala/docspell/analysis/classifier/StanfordTextClassifier.scala @@ -21,7 +21,7 @@ import docspell.logging.Logger import edu.stanford.nlp.classify.ColumnDataClassifier -final class StanfordTextClassifier[F[_]: Async](cfg: TextClassifierConfig) +final class StanfordTextClassifier[F[_]: Async: Files](cfg: TextClassifierConfig) extends TextClassifier[F] { def trainClassifier[A]( diff --git a/modules/analysis/src/main/scala/docspell/analysis/nlp/PipelineCache.scala b/modules/analysis/src/main/scala/docspell/analysis/nlp/PipelineCache.scala index 28f9490b..03db7afb 100644 --- a/modules/analysis/src/main/scala/docspell/analysis/nlp/PipelineCache.scala +++ b/modules/analysis/src/main/scala/docspell/analysis/nlp/PipelineCache.scala @@ -11,6 +11,7 @@ import scala.concurrent.duration.{Duration => _, _} import cats.effect.Ref import cats.effect._ import cats.implicits._ +import fs2.io.file.Files import docspell.analysis.NlpSettings import docspell.common._ @@ -32,7 +33,7 @@ trait PipelineCache[F[_]] { object PipelineCache { private[this] val logger = docspell.logging.unsafeLogger - def apply[F[_]: Async](clearInterval: Duration)( + def apply[F[_]: Async: Files](clearInterval: Duration)( creator: NlpSettings => Annotator[F], release: F[Unit] ): F[PipelineCache[F]] = { @@ -44,7 +45,7 @@ object PipelineCache { } yield new Impl[F](data, creator, cacheClear) } - final private class Impl[F[_]: Async]( + final private class Impl[F[_]: Async: Files]( data: Ref[F, Map[String, Entry[Annotator[F]]]], creator: NlpSettings => Annotator[F], cacheClear: CacheClearing[F] diff --git a/modules/backend/src/main/scala/docspell/backend/BackendApp.scala b/modules/backend/src/main/scala/docspell/backend/BackendApp.scala index 365cdd00..8ebfa2ae 100644 --- a/modules/backend/src/main/scala/docspell/backend/BackendApp.scala +++ b/modules/backend/src/main/scala/docspell/backend/BackendApp.scala @@ -7,6 +7,7 @@ package docspell.backend import cats.effect._ +import fs2.io.file.Files import docspell.backend.BackendCommands.EventContext import docspell.backend.auth.Login @@ -65,7 +66,7 @@ trait BackendApp[F[_]] { object BackendApp { - def create[F[_]: Async]( + def create[F[_]: Async: Files]( cfg: Config, store: Store[F], javaEmil: Emil[F], diff --git a/modules/backend/src/main/scala/docspell/backend/joex/AddonOps.scala b/modules/backend/src/main/scala/docspell/backend/joex/AddonOps.scala index 3fe95980..1cfa2a9e 100644 --- a/modules/backend/src/main/scala/docspell/backend/joex/AddonOps.scala +++ b/modules/backend/src/main/scala/docspell/backend/joex/AddonOps.scala @@ -9,6 +9,7 @@ package docspell.backend.joex import cats.data.OptionT import cats.effect._ import cats.syntax.all._ +import fs2.io.file.Files import docspell.addons._ import docspell.backend.joex.AddonOps.{AddonRunConfigRef, ExecResult} @@ -98,7 +99,7 @@ object AddonOps { ) } - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( cfg: AddonEnvConfig, store: Store[F], cmdRunner: BackendCommandRunner[F, Unit], @@ -160,7 +161,10 @@ object AddonOps { execRes = ExecResult(List(result), List(runCfg)) } yield execRes - def createMiddleware(custom: Middleware[F], runCfg: AddonRunConfigRef) = for { + def createMiddleware( + custom: Middleware[F], + runCfg: AddonRunConfigRef + ): F[Middleware[F]] = for { dscMW <- prepare.createDscEnv(runCfg, cfg.executorConfig.runTimeout) mm = dscMW >> custom >> prepare.logResult(logger, runCfg) >> Middleware .ephemeralRun[F] diff --git a/modules/backend/src/main/scala/docspell/backend/ops/AddonValidate.scala b/modules/backend/src/main/scala/docspell/backend/ops/AddonValidate.scala index 73e783ac..9863d8fb 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/AddonValidate.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/AddonValidate.scala @@ -10,7 +10,7 @@ import cats.data.EitherT import cats.effect._ import cats.syntax.all._ import fs2.Stream -import fs2.io.file.Path +import fs2.io.file.{Files, Path} import docspell.addons.{AddonMeta, RunnerType} import docspell.backend.Config @@ -21,7 +21,7 @@ import docspell.joexapi.model.AddonSupport import docspell.store.Store import docspell.store.records.RAddonArchive -final class AddonValidate[F[_]: Async]( +final class AddonValidate[F[_]: Async: Files]( cfg: Config.Addons, store: Store[F], joexOps: OJoex[F] diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OAddons.scala b/modules/backend/src/main/scala/docspell/backend/ops/OAddons.scala index 4426658b..04e83983 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/OAddons.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/OAddons.scala @@ -9,6 +9,7 @@ package docspell.backend.ops import cats.data.{EitherT, NonEmptyList, OptionT} import cats.effect._ import cats.syntax.all._ +import fs2.io.file.Files import docspell.addons.{AddonMeta, AddonTriggerType} import docspell.backend.ops.AddonValidationError._ @@ -129,7 +130,7 @@ object OAddons { def failure[A](error: AddonValidationError): AddonValidationResult[A] = Left(error) } - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( cfg: Config.Addons, store: Store[F], userTasks: UserTaskStore[F], diff --git a/modules/common/src/main/scala/docspell/common/Binary.scala b/modules/common/src/main/scala/docspell/common/Binary.scala index 409ee6d4..5fd83e2f 100644 --- a/modules/common/src/main/scala/docspell/common/Binary.scala +++ b/modules/common/src/main/scala/docspell/common/Binary.scala @@ -39,7 +39,7 @@ final case class Binary[F[_]](name: String, mime: MimeType, data: Stream[F, Byte object Binary { - def apply[F[_]: Async](file: Path): Binary[F] = + def apply[F[_]: Files](file: Path): Binary[F] = Binary(file.fileName.toString, Files[F].readAll(file)) def apply[F[_]](name: String, data: Stream[F, Byte]): Binary[F] = @@ -74,11 +74,11 @@ object Binary { data.chunks.map(_.toByteVector).compile.fold(ByteVector.empty)((r, e) => r ++ e) /** Convert paths into `Binary`s */ - def toBinary[F[_]: Async]: Pipe[F, Path, Binary[F]] = + def toBinary[F[_]: Files]: Pipe[F, Path, Binary[F]] = _.map(Binary[F](_)) /** Save one or more binaries to a target directory. */ - def saveTo[F[_]: Async]( + def saveTo[F[_]: Async: Files]( logger: Logger[F], targetDir: Path ): Pipe[F, Binary[F], Path] = diff --git a/modules/common/src/main/scala/docspell/common/util/File.scala b/modules/common/src/main/scala/docspell/common/util/File.scala index 679c99ec..f31fd8ae 100644 --- a/modules/common/src/main/scala/docspell/common/util/File.scala +++ b/modules/common/src/main/scala/docspell/common/util/File.scala @@ -72,6 +72,6 @@ object File { .drain .map(_ => file) - def readJson[F[_]: Async, A](file: Path)(implicit d: Decoder[A]): F[A] = + def readJson[F[_]: Async: Files, A](file: Path)(implicit d: Decoder[A]): F[A] = readText[F](file).map(parser.decode[A]).rethrow } diff --git a/modules/common/src/main/scala/docspell/common/util/Zip.scala b/modules/common/src/main/scala/docspell/common/util/Zip.scala index 335ed0b5..ca89ba10 100644 --- a/modules/common/src/main/scala/docspell/common/util/Zip.scala +++ b/modules/common/src/main/scala/docspell/common/util/Zip.scala @@ -7,7 +7,7 @@ package docspell.common.util import cats.effect._ -import fs2.io.file.Path +import fs2.io.file.{Files, Path} import fs2.{Pipe, Stream} import docspell.common.Glob @@ -33,9 +33,9 @@ trait Zip[F[_]] { } object Zip { - val defaultChunkSize = 64 * 1024 + private val defaultChunkSize = 64 * 1024 - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( logger: Option[Logger[F]] = None, tempDir: Option[Path] = None ): Zip[F] = diff --git a/modules/common/src/main/scala/docspell/common/util/ZipImpl.scala b/modules/common/src/main/scala/docspell/common/util/ZipImpl.scala index be65eee2..2260b0e8 100644 --- a/modules/common/src/main/scala/docspell/common/util/ZipImpl.scala +++ b/modules/common/src/main/scala/docspell/common/util/ZipImpl.scala @@ -22,7 +22,7 @@ import fs2.{Chunk, Pipe, Stream} import docspell.common.Glob import docspell.logging.Logger -final private class ZipImpl[F[_]: Async]( +final private class ZipImpl[F[_]: Async: Files]( log: Option[Logger[F]], tempDir: Option[Path] ) extends Zip[F] { diff --git a/modules/config/src/main/scala/docspell/config/ConfigFactory.scala b/modules/config/src/main/scala/docspell/config/ConfigFactory.scala index 3522a48e..6af72e37 100644 --- a/modules/config/src/main/scala/docspell/config/ConfigFactory.scala +++ b/modules/config/src/main/scala/docspell/config/ConfigFactory.scala @@ -27,7 +27,10 @@ object ConfigFactory { * 1. if no file is found, read the config from environment variables falling back to * the default config */ - def default[F[_]: Async, C: ClassTag: ConfigReader](logger: Logger[F], atPath: String)( + def default[F[_]: Async: Files, C: ClassTag: ConfigReader]( + logger: Logger[F], + atPath: String + )( args: List[String], validation: Validation[C] ): F[C] = @@ -74,7 +77,7 @@ object ConfigFactory { /** Uses the first argument as a path to the config file. If it is specified but the * file doesn't exist, an exception is thrown. */ - private def findFileFromArgs[F[_]: Async](args: List[String]): F[Option[Path]] = + private def findFileFromArgs[F[_]: Async: Files](args: List[String]): F[Option[Path]] = args.headOption .map(Path.apply) .traverse(p => @@ -89,7 +92,7 @@ object ConfigFactory { * to giving the file as argument, it is not an error to specify a non-existing file * via a system property. */ - private def checkSystemProperty[F[_]: Async]: OptionT[F, Path] = + private def checkSystemProperty[F[_]: Async: Files]: OptionT[F, Path] = for { cf <- OptionT( Sync[F].delay( diff --git a/modules/convert/src/main/scala/docspell/convert/Conversion.scala b/modules/convert/src/main/scala/docspell/convert/Conversion.scala index ab9cce88..6ab0c149 100644 --- a/modules/convert/src/main/scala/docspell/convert/Conversion.scala +++ b/modules/convert/src/main/scala/docspell/convert/Conversion.scala @@ -11,6 +11,7 @@ import java.nio.charset.StandardCharsets import cats.effect._ import cats.implicits._ import fs2._ +import fs2.io.file.Files import docspell.common._ import docspell.convert.ConversionResult.Handler @@ -32,7 +33,7 @@ trait Conversion[F[_]] { object Conversion { - def create[F[_]: Async]( + def create[F[_]: Async: Files]( cfg: ConvertConfig, sanitizeHtml: SanitizeHtml, additionalPasswords: List[Password], diff --git a/modules/convert/src/main/scala/docspell/convert/extern/ExternConv.scala b/modules/convert/src/main/scala/docspell/convert/extern/ExternConv.scala index 8f0f9e11..5f1253f5 100644 --- a/modules/convert/src/main/scala/docspell/convert/extern/ExternConv.scala +++ b/modules/convert/src/main/scala/docspell/convert/extern/ExternConv.scala @@ -19,7 +19,7 @@ import docspell.logging.Logger private[extern] object ExternConv { - def toPDF[F[_]: Async, A]( + def toPDF[F[_]: Async: Files, A]( name: String, cmdCfg: SystemCommand.Config, wd: Path, @@ -71,7 +71,7 @@ private[extern] object ExternConv { handler.run(ConversionResult.failure(ex)) } - def readResult[F[_]: Async]( + def readResult[F[_]: Async: Files]( chunkSize: Int, logger: Logger[F] )(out: Path, result: SystemCommand.Result): F[ConversionResult[F]] = @@ -99,7 +99,7 @@ private[extern] object ExternConv { .pure[F] } - def readResultTesseract[F[_]: Async]( + def readResultTesseract[F[_]: Async: Files]( outPrefix: String, chunkSize: Int, logger: Logger[F] @@ -127,7 +127,7 @@ private[extern] object ExternConv { } } - private def storeDataToFile[F[_]: Async]( + private def storeDataToFile[F[_]: Async: Files]( name: String, logger: Logger[F], inFile: Path @@ -146,7 +146,7 @@ private[extern] object ExternConv { logger.debug(s"$name stdout: ${result.stdout}") *> logger.debug(s"$name stderr: ${result.stderr}") - private def storeFile[F[_]: Async]( + private def storeFile[F[_]: Async: Files]( in: Stream[F, Byte], target: Path ): F[Unit] = diff --git a/modules/convert/src/main/scala/docspell/convert/extern/OcrMyPdf.scala b/modules/convert/src/main/scala/docspell/convert/extern/OcrMyPdf.scala index d133b4dd..1fc778ef 100644 --- a/modules/convert/src/main/scala/docspell/convert/extern/OcrMyPdf.scala +++ b/modules/convert/src/main/scala/docspell/convert/extern/OcrMyPdf.scala @@ -8,7 +8,7 @@ package docspell.convert.extern import cats.effect._ import fs2.Stream -import fs2.io.file.Path +import fs2.io.file.{Files, Path} import docspell.common._ import docspell.convert.ConversionResult @@ -17,7 +17,7 @@ import docspell.logging.Logger object OcrMyPdf { - def toPDF[F[_]: Async, A]( + def toPDF[F[_]: Async: Files, A]( cfg: OcrMyPdfConfig, lang: Language, chunkSize: Int, diff --git a/modules/convert/src/main/scala/docspell/convert/extern/Tesseract.scala b/modules/convert/src/main/scala/docspell/convert/extern/Tesseract.scala index bd2ae95d..95875f8f 100644 --- a/modules/convert/src/main/scala/docspell/convert/extern/Tesseract.scala +++ b/modules/convert/src/main/scala/docspell/convert/extern/Tesseract.scala @@ -8,7 +8,7 @@ package docspell.convert.extern import cats.effect._ import fs2.Stream -import fs2.io.file.Path +import fs2.io.file.{Files, Path} import docspell.common._ import docspell.convert.ConversionResult @@ -17,7 +17,7 @@ import docspell.logging.Logger object Tesseract { - def toPDF[F[_]: Async, A]( + def toPDF[F[_]: Async: Files, A]( cfg: TesseractConfig, lang: Language, chunkSize: Int, diff --git a/modules/convert/src/main/scala/docspell/convert/extern/Unoconv.scala b/modules/convert/src/main/scala/docspell/convert/extern/Unoconv.scala index efe0efa7..cbe0db87 100644 --- a/modules/convert/src/main/scala/docspell/convert/extern/Unoconv.scala +++ b/modules/convert/src/main/scala/docspell/convert/extern/Unoconv.scala @@ -8,7 +8,7 @@ package docspell.convert.extern import cats.effect._ import fs2.Stream -import fs2.io.file.Path +import fs2.io.file.{Files, Path} import docspell.common._ import docspell.convert.ConversionResult @@ -17,7 +17,7 @@ import docspell.logging.Logger object Unoconv { - def toPDF[F[_]: Async, A]( + def toPDF[F[_]: Async: Files, A]( cfg: UnoconvConfig, chunkSize: Int, logger: Logger[F] diff --git a/modules/convert/src/main/scala/docspell/convert/extern/Weasyprint.scala b/modules/convert/src/main/scala/docspell/convert/extern/Weasyprint.scala index ba1d6ce7..2470d0fe 100644 --- a/modules/convert/src/main/scala/docspell/convert/extern/Weasyprint.scala +++ b/modules/convert/src/main/scala/docspell/convert/extern/Weasyprint.scala @@ -10,7 +10,7 @@ import java.nio.charset.Charset import cats.effect._ import cats.implicits._ -import fs2.io.file.Path +import fs2.io.file.{Files, Path} import fs2.{Chunk, Stream} import docspell.common._ @@ -20,7 +20,7 @@ import docspell.logging.Logger object Weasyprint { - def toPDF[F[_]: Async, A]( + def toPDF[F[_]: Async: Files, A]( cfg: WeasyprintConfig, chunkSize: Int, charset: Charset, @@ -46,7 +46,7 @@ object Weasyprint { ) ExternConv - .toPDF[F, A]("weasyprint", cmdCfg, cfg.workingDir, true, logger, reader)( + .toPDF[F, A]("weasyprint", cmdCfg, cfg.workingDir, useStdin = true, logger, reader)( inSane, handler ) diff --git a/modules/convert/src/main/scala/docspell/convert/extern/WkHtmlPdf.scala b/modules/convert/src/main/scala/docspell/convert/extern/WkHtmlPdf.scala index d3ed16c0..04e973fe 100644 --- a/modules/convert/src/main/scala/docspell/convert/extern/WkHtmlPdf.scala +++ b/modules/convert/src/main/scala/docspell/convert/extern/WkHtmlPdf.scala @@ -10,7 +10,7 @@ import java.nio.charset.Charset import cats.effect._ import cats.implicits._ -import fs2.io.file.Path +import fs2.io.file.{Files, Path} import fs2.{Chunk, Stream} import docspell.common._ @@ -20,7 +20,7 @@ import docspell.logging.Logger object WkHtmlPdf { - def toPDF[F[_]: Async, A]( + def toPDF[F[_]: Async: Files, A]( cfg: WkHtmlPdfConfig, chunkSize: Int, charset: Charset, @@ -46,7 +46,14 @@ object WkHtmlPdf { ) ExternConv - .toPDF[F, A]("wkhtmltopdf", cmdCfg, cfg.workingDir, true, logger, reader)( + .toPDF[F, A]( + "wkhtmltopdf", + cmdCfg, + cfg.workingDir, + useStdin = true, + logger, + reader + )( inSane, handler ) diff --git a/modules/extract/src/main/scala/docspell/extract/Extraction.scala b/modules/extract/src/main/scala/docspell/extract/Extraction.scala index c1a27023..b57b9f99 100644 --- a/modules/extract/src/main/scala/docspell/extract/Extraction.scala +++ b/modules/extract/src/main/scala/docspell/extract/Extraction.scala @@ -9,6 +9,7 @@ package docspell.extract import cats.effect._ import cats.implicits._ import fs2.Stream +import fs2.io.file.Files import docspell.common._ import docspell.extract.internal.Text @@ -32,7 +33,7 @@ trait Extraction[F[_]] { object Extraction { - def create[F[_]: Async]( + def create[F[_]: Async: Files]( logger: Logger[F], cfg: ExtractConfig ): Extraction[F] = diff --git a/modules/extract/src/main/scala/docspell/extract/PdfExtract.scala b/modules/extract/src/main/scala/docspell/extract/PdfExtract.scala index 400a1a4e..8d6e7208 100644 --- a/modules/extract/src/main/scala/docspell/extract/PdfExtract.scala +++ b/modules/extract/src/main/scala/docspell/extract/PdfExtract.scala @@ -9,6 +9,7 @@ package docspell.extract import cats.effect._ import cats.implicits._ import fs2.Stream +import fs2.io.file.Files import docspell.common.Language import docspell.extract.internal.Text @@ -24,7 +25,7 @@ object PdfExtract { Result(t._1, t._2) } - def get[F[_]: Async]( + def get[F[_]: Async: Files]( in: Stream[F, Byte], lang: Language, stripMinLen: Int, diff --git a/modules/extract/src/main/scala/docspell/extract/ocr/Ocr.scala b/modules/extract/src/main/scala/docspell/extract/ocr/Ocr.scala index f39e4b0b..b0828201 100644 --- a/modules/extract/src/main/scala/docspell/extract/ocr/Ocr.scala +++ b/modules/extract/src/main/scala/docspell/extract/ocr/Ocr.scala @@ -8,7 +8,7 @@ package docspell.extract.ocr import cats.effect._ import fs2.Stream -import fs2.io.file.Path +import fs2.io.file.{Files, Path} import docspell.common._ import docspell.common.util.File @@ -17,7 +17,7 @@ import docspell.logging.Logger object Ocr { /** Extract the text of all pages in the given pdf file. */ - def extractPdf[F[_]: Async]( + def extractPdf[F[_]: Async: Files]( pdf: Stream[F, Byte], logger: Logger[F], lang: String, @@ -40,7 +40,7 @@ object Ocr { ): Stream[F, String] = runTesseractStdin(img, logger, lang, config) - def extractPdFFile[F[_]: Async]( + def extractPdFFile[F[_]: Async: Files]( pdf: Path, logger: Logger[F], lang: String, @@ -65,7 +65,7 @@ object Ocr { /** Run ghostscript to extract all pdf pages into tiff files. The files are stored to a * temporary location on disk and returned. */ - private[extract] def runGhostscript[F[_]: Async]( + private[extract] def runGhostscript[F[_]: Async: Files]( pdf: Stream[F, Byte], cfg: OcrConfig, wd: Path, @@ -91,7 +91,7 @@ object Ocr { /** Run ghostscript to extract all pdf pages into tiff files. The files are stored to a * temporary location on disk and returned. */ - private[extract] def runGhostscriptFile[F[_]: Async]( + private[extract] def runGhostscriptFile[F[_]: Async: Files]( pdf: Path, ghostscript: SystemCommand.Config, wd: Path, diff --git a/modules/extract/src/main/scala/docspell/extract/ocr/TextExtract.scala b/modules/extract/src/main/scala/docspell/extract/ocr/TextExtract.scala index a08a2cee..0fc8123e 100644 --- a/modules/extract/src/main/scala/docspell/extract/ocr/TextExtract.scala +++ b/modules/extract/src/main/scala/docspell/extract/ocr/TextExtract.scala @@ -8,6 +8,7 @@ package docspell.extract.ocr import cats.effect._ import fs2.Stream +import fs2.io.file.Files import docspell.common._ import docspell.extract.internal.Text @@ -16,7 +17,7 @@ import docspell.logging.Logger object TextExtract { - def extract[F[_]: Async]( + def extract[F[_]: Async: Files]( in: Stream[F, Byte], logger: Logger[F], lang: String, @@ -24,7 +25,7 @@ object TextExtract { ): Stream[F, Text] = extractOCR(in, logger, lang, config) - def extractOCR[F[_]: Async]( + def extractOCR[F[_]: Async: Files]( in: Stream[F, Byte], logger: Logger[F], lang: String, diff --git a/modules/files/src/main/scala/docspell/files/FileSupport.scala b/modules/files/src/main/scala/docspell/files/FileSupport.scala index bdd19197..5f924445 100644 --- a/modules/files/src/main/scala/docspell/files/FileSupport.scala +++ b/modules/files/src/main/scala/docspell/files/FileSupport.scala @@ -7,7 +7,7 @@ package docspell.files import cats.data.OptionT -import cats.effect.{Async, Sync} +import cats.effect.Sync import cats.syntax.all._ import fs2.Pipe import fs2.io.file.{Files, Path} @@ -39,7 +39,7 @@ trait FileSupport { TikaMimetype.detect[F](bin.data, hint).map(mt => bin.copy(mime = mt)) } - def toBinaryWithMime[F[_]: Async]: Pipe[F, Path, Binary[F]] = + def toBinaryWithMime[F[_]: Sync: Files]: Pipe[F, Path, Binary[F]] = _.evalMap(file => file.mimeType.map(mt => Binary(file).copy(mime = mt))) } diff --git a/modules/joex/src/main/scala/docspell/joex/ConfigFile.scala b/modules/joex/src/main/scala/docspell/joex/ConfigFile.scala index f32378bb..059f28a7 100644 --- a/modules/joex/src/main/scala/docspell/joex/ConfigFile.scala +++ b/modules/joex/src/main/scala/docspell/joex/ConfigFile.scala @@ -7,6 +7,7 @@ package docspell.joex import cats.effect.Async +import fs2.io.file.Files import docspell.config.Implicits._ import docspell.config.{ConfigFactory, FtsType, Validation} @@ -25,7 +26,7 @@ object ConfigFile { // IntelliJ is wrong, this is required import Implicits._ - def loadConfig[F[_]: Async](args: List[String]): F[Config] = { + def loadConfig[F[_]: Async: Files](args: List[String]): F[Config] = { val logger = docspell.logging.getLogger[F] ConfigFactory .default[F, Config](logger, "docspell.joex")(args, validate) diff --git a/modules/joex/src/main/scala/docspell/joex/JoexAppImpl.scala b/modules/joex/src/main/scala/docspell/joex/JoexAppImpl.scala index bf9e0137..ebf57d44 100644 --- a/modules/joex/src/main/scala/docspell/joex/JoexAppImpl.scala +++ b/modules/joex/src/main/scala/docspell/joex/JoexAppImpl.scala @@ -9,6 +9,8 @@ package docspell.joex import cats.effect._ import cats.implicits._ import fs2.concurrent.SignallingRef +import fs2.io.file.Files +import fs2.io.net.Network import docspell.backend.MailAddressCodec import docspell.backend.joex.FindJobOwnerAccount @@ -45,7 +47,7 @@ final class JoexAppImpl[F[_]: Async]( def init: F[Unit] = { val run = scheduler.start.compile.drain val prun = periodicScheduler.start.compile.drain - val eventConsume = notificationMod.consumeAllEvents(2).compile.drain + val eventConsume = notificationMod.consumeAllEvents(maxConcurrent = 2).compile.drain for { _ <- scheduleBackgroundTasks _ <- Async[F].start(run) @@ -62,7 +64,9 @@ final class JoexAppImpl[F[_]: Async]( store.transact(RJobLog.findLogs(jobId)) def initShutdown: F[Unit] = - periodicScheduler.shutdown *> scheduler.shutdown(false) *> termSignal.set(true) + periodicScheduler.shutdown *> scheduler.shutdown(cancelAll = false) *> termSignal.set( + true + ) private def scheduleBackgroundTasks: F[Unit] = HouseKeepingTask @@ -81,7 +85,8 @@ final class JoexAppImpl[F[_]: Async]( private def scheduleEmptyTrashTasks: F[Unit] = store .transact( - REmptyTrashSetting.findForAllCollectives(OCollective.EmptyTrash.default, 50) + REmptyTrashSetting + .findForAllCollectives(OCollective.EmptyTrash.default, chunkSize = 50) ) .evalMap { es => val args = EmptyTrashArgs(es.cid, es.minAge) @@ -98,7 +103,7 @@ final class JoexAppImpl[F[_]: Async]( object JoexAppImpl extends MailAddressCodec { - def create[F[_]: Async]( + def create[F[_]: Async: Files: Network]( cfg: Config, termSignal: SignallingRef[F, Boolean], store: Store[F], @@ -107,12 +112,14 @@ object JoexAppImpl extends MailAddressCodec { pools: Pools ): Resource[F, JoexApp[F]] = for { - joexLogger <- Resource.pure(docspell.logging.getLogger[F](s"joex-${cfg.appId.id}")) + joexLogger <- Resource.pure( + docspell.logging.getLogger[F](name = s"joex-${cfg.appId.id}") + ) pubSubT = PubSubT(pubSub, joexLogger) javaEmil = JavaMailEmil(Settings.defaultSettings.copy(debug = cfg.mailDebug)) notificationMod <- Resource.eval( - NotificationModuleImpl[F](store, javaEmil, httpClient, 200) + NotificationModuleImpl[F](store, javaEmil, httpClient, queueSize = 200) ) jobStoreModule = JobStoreModuleBuilder(store) diff --git a/modules/joex/src/main/scala/docspell/joex/JoexServer.scala b/modules/joex/src/main/scala/docspell/joex/JoexServer.scala index e138ba38..e527f811 100644 --- a/modules/joex/src/main/scala/docspell/joex/JoexServer.scala +++ b/modules/joex/src/main/scala/docspell/joex/JoexServer.scala @@ -9,6 +9,8 @@ package docspell.joex import cats.effect._ import fs2.Stream import fs2.concurrent.SignallingRef +import fs2.io.file.Files +import fs2.io.net.Network import docspell.backend.msg.Topics import docspell.common.Pools @@ -32,7 +34,10 @@ object JoexServer { exitRef: Ref[F, ExitCode] ) - def stream[F[_]: Async](cfg: Config, pools: Pools): Stream[F, Nothing] = { + def stream[F[_]: Async: Files: Network]( + cfg: Config, + pools: Pools + ): Stream[F, Nothing] = { val app = for { signal <- Resource.eval(SignallingRef[F, Boolean](false)) diff --git a/modules/joex/src/main/scala/docspell/joex/JoexTasks.scala b/modules/joex/src/main/scala/docspell/joex/JoexTasks.scala index 7114be6d..639dfa49 100644 --- a/modules/joex/src/main/scala/docspell/joex/JoexTasks.scala +++ b/modules/joex/src/main/scala/docspell/joex/JoexTasks.scala @@ -7,6 +7,8 @@ package docspell.joex import cats.effect.{Async, Resource} +import fs2.io.file.Files +import fs2.io.net.Network import docspell.analysis.TextAnalyser import docspell.backend.BackendCommands @@ -46,7 +48,7 @@ import docspell.store.Store import emil.Emil import org.http4s.client.Client -final class JoexTasks[F[_]: Async]( +final class JoexTasks[F[_]: Async: Files: Network]( cfg: Config, store: Store[F], itemOps: OItem[F], @@ -257,7 +259,7 @@ final class JoexTasks[F[_]: Async]( object JoexTasks { - def resource[F[_]: Async]( + def resource[F[_]: Async: Files: Network]( cfg: Config, pools: Pools, jobStoreModule: JobStoreModuleBuilder.Module[F], diff --git a/modules/joex/src/main/scala/docspell/joex/addon/GenericItemAddonTask.scala b/modules/joex/src/main/scala/docspell/joex/addon/GenericItemAddonTask.scala index 37cc0ef7..bbe9e2eb 100644 --- a/modules/joex/src/main/scala/docspell/joex/addon/GenericItemAddonTask.scala +++ b/modules/joex/src/main/scala/docspell/joex/addon/GenericItemAddonTask.scala @@ -43,7 +43,7 @@ object GenericItemAddonTask extends LoggerExtension { "ITEM_PDF_JSON" -> pdfMetaJson ) - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( ops: AddonOps[F], store: Store[F], trigger: AddonTriggerType, @@ -57,7 +57,7 @@ object GenericItemAddonTask extends LoggerExtension { data ) - def addonResult[F[_]: Async]( + def addonResult[F[_]: Async: Files]( ops: AddonOps[F], store: Store[F], trigger: AddonTriggerType, @@ -73,7 +73,7 @@ object GenericItemAddonTask extends LoggerExtension { ) } - def prepareItemData[F[_]: Async]( + def prepareItemData[F[_]: Async: Files]( logger: Logger[F], store: Store[F], data: ItemData, diff --git a/modules/joex/src/main/scala/docspell/joex/addon/ItemAddonTask.scala b/modules/joex/src/main/scala/docspell/joex/addon/ItemAddonTask.scala index 32f8bc7b..8dfb0a3a 100644 --- a/modules/joex/src/main/scala/docspell/joex/addon/ItemAddonTask.scala +++ b/modules/joex/src/main/scala/docspell/joex/addon/ItemAddonTask.scala @@ -9,6 +9,7 @@ package docspell.joex.addon import cats.data.OptionT import cats.effect._ import cats.syntax.all._ +import fs2.io.file.Files import docspell.addons.AddonTriggerType import docspell.backend.joex.AddonOps @@ -26,7 +27,10 @@ object ItemAddonTask extends AddonTaskExtension { def onCancel[F[_]]: Task[F, Args, Unit] = Task.log(_.warn(s"Cancelling ${name.id} task")) - def apply[F[_]: Async](ops: AddonOps[F], store: Store[F]): Task[F, Args, Result] = + def apply[F[_]: Async: Files]( + ops: AddonOps[F], + store: Store[F] + ): Task[F, Args, Result] = Task { ctx => (for { item <- OptionT( diff --git a/modules/joex/src/main/scala/docspell/joex/analysis/NerFile.scala b/modules/joex/src/main/scala/docspell/joex/analysis/NerFile.scala index 52f51ea9..0077b6e5 100644 --- a/modules/joex/src/main/scala/docspell/joex/analysis/NerFile.scala +++ b/modules/joex/src/main/scala/docspell/joex/analysis/NerFile.scala @@ -8,7 +8,7 @@ package docspell.joex.analysis import cats.effect._ import cats.implicits._ -import fs2.io.file.Path +import fs2.io.file.{Files, Path} import docspell.analysis.split.TextSplitter import docspell.common._ @@ -39,7 +39,7 @@ object NerFile { private def jsonFilePath(directory: Path, collective: CollectiveId): Path = directory.resolve(s"${collective.value}.json") - def find[F[_]: Async]( + def find[F[_]: Async: Files]( collective: CollectiveId, directory: Path ): F[Option[NerFile]] = { diff --git a/modules/joex/src/main/scala/docspell/joex/analysis/RegexNerFile.scala b/modules/joex/src/main/scala/docspell/joex/analysis/RegexNerFile.scala index 8801bff3..327a725e 100644 --- a/modules/joex/src/main/scala/docspell/joex/analysis/RegexNerFile.scala +++ b/modules/joex/src/main/scala/docspell/joex/analysis/RegexNerFile.scala @@ -9,7 +9,7 @@ package docspell.joex.analysis import cats.effect._ import cats.effect.std.Semaphore import cats.implicits._ -import fs2.io.file.Path +import fs2.io.file.{Files, Path} import docspell.common._ import docspell.common.util.File @@ -32,7 +32,7 @@ object RegexNerFile { case class Config(maxEntries: Int, directory: Path, minTime: Duration) - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( cfg: Config, store: Store[F] ): Resource[F, RegexNerFile[F]] = @@ -41,7 +41,7 @@ object RegexNerFile { writer <- Resource.eval(Semaphore(1)) } yield new Impl[F](cfg.copy(directory = dir), store, writer) - final private class Impl[F[_]: Async]( + final private class Impl[F[_]: Async: Files]( cfg: Config, store: Store[F], writer: Semaphore[F] // TODO allow parallelism per collective diff --git a/modules/joex/src/main/scala/docspell/joex/download/DownloadZipTask.scala b/modules/joex/src/main/scala/docspell/joex/download/DownloadZipTask.scala index 92856b9b..3d8b9000 100644 --- a/modules/joex/src/main/scala/docspell/joex/download/DownloadZipTask.scala +++ b/modules/joex/src/main/scala/docspell/joex/download/DownloadZipTask.scala @@ -10,6 +10,7 @@ import java.time.format.DateTimeFormatter import cats.effect._ import cats.syntax.all._ +import fs2.io.file.Files import fs2.{Pipe, Stream} import docspell.backend.ops.ODownloadAll @@ -28,7 +29,7 @@ object DownloadZipTask { def onCancel[F[_]]: Task[F, Args, Unit] = Task.log(_.warn(s"Cancelling ${DownloadZipArgs.taskName.id} task")) - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( chunkSize: Int, store: Store[F], downloadOps: ODownloadAll[F] diff --git a/modules/joex/src/main/scala/docspell/joex/hk/CheckNodesTask.scala b/modules/joex/src/main/scala/docspell/joex/hk/CheckNodesTask.scala index 7f9de5ae..26552511 100644 --- a/modules/joex/src/main/scala/docspell/joex/hk/CheckNodesTask.scala +++ b/modules/joex/src/main/scala/docspell/joex/hk/CheckNodesTask.scala @@ -8,6 +8,7 @@ package docspell.joex.hk import cats.effect._ import cats.implicits._ +import fs2.io.net.Network import docspell.common._ import docspell.logging.Logger @@ -19,7 +20,7 @@ import org.http4s.client.Client import org.http4s.ember.client.EmberClientBuilder object CheckNodesTask { - def apply[F[_]: Async]( + def apply[F[_]: Async: Network]( cfg: HouseKeepingConfig.CheckNodes, store: Store[F] ): Task[F, Unit, CleanupResult] = diff --git a/modules/joex/src/main/scala/docspell/joex/hk/HouseKeepingTask.scala b/modules/joex/src/main/scala/docspell/joex/hk/HouseKeepingTask.scala index 3d7d88cd..519cfa03 100644 --- a/modules/joex/src/main/scala/docspell/joex/hk/HouseKeepingTask.scala +++ b/modules/joex/src/main/scala/docspell/joex/hk/HouseKeepingTask.scala @@ -8,6 +8,7 @@ package docspell.joex.hk import cats.effect._ import cats.implicits._ +import fs2.io.net.Network import docspell.backend.ops.{ODownloadAll, OFileRepository} import docspell.common._ @@ -26,7 +27,7 @@ object HouseKeepingTask { val taskName: Ident = Ident.unsafe("housekeeping") - def apply[F[_]: Async]( + def apply[F[_]: Async: Network]( cfg: Config, store: Store[F], fileRepo: OFileRepository[F], diff --git a/modules/joex/src/main/scala/docspell/joex/learn/Classify.scala b/modules/joex/src/main/scala/docspell/joex/learn/Classify.scala index 9812e619..92db0c19 100644 --- a/modules/joex/src/main/scala/docspell/joex/learn/Classify.scala +++ b/modules/joex/src/main/scala/docspell/joex/learn/Classify.scala @@ -21,7 +21,7 @@ import docspell.store.records.RClassifierModel object Classify { - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( logger: Logger[F], workingDir: Path, store: Store[F], diff --git a/modules/joex/src/main/scala/docspell/joex/learn/LearnClassifierTask.scala b/modules/joex/src/main/scala/docspell/joex/learn/LearnClassifierTask.scala index 06e9ad83..74dc4cdb 100644 --- a/modules/joex/src/main/scala/docspell/joex/learn/LearnClassifierTask.scala +++ b/modules/joex/src/main/scala/docspell/joex/learn/LearnClassifierTask.scala @@ -9,6 +9,7 @@ package docspell.joex.learn import cats.data.OptionT import cats.effect._ import cats.implicits._ +import fs2.io.file.Files import docspell.analysis.TextAnalyser import docspell.backend.ops.OCollective @@ -28,7 +29,7 @@ object LearnClassifierTask { def onCancel[F[_]]: Task[F, Args, Unit] = Task.log(_.warn("Cancelling learn-classifier task")) - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( cfg: Config.TextAnalysis, store: Store[F], analyser: TextAnalyser[F] @@ -37,7 +38,7 @@ object LearnClassifierTask { .flatMap(_ => learnItemEntities(cfg, store, analyser)) .flatMap(_ => Task(_ => Sync[F].delay(System.gc()))) - private def learnItemEntities[F[_]: Async]( + private def learnItemEntities[F[_]: Async: Files]( cfg: Config.TextAnalysis, store: Store[F], analyser: TextAnalyser[F] @@ -56,7 +57,7 @@ object LearnClassifierTask { else ().pure[F] } - private def learnTags[F[_]: Async]( + private def learnTags[F[_]: Async: Files]( cfg: Config.TextAnalysis, store: Store[F], analyser: TextAnalyser[F] diff --git a/modules/joex/src/main/scala/docspell/joex/learn/LearnItemEntities.scala b/modules/joex/src/main/scala/docspell/joex/learn/LearnItemEntities.scala index d394b536..8015e11f 100644 --- a/modules/joex/src/main/scala/docspell/joex/learn/LearnItemEntities.scala +++ b/modules/joex/src/main/scala/docspell/joex/learn/LearnItemEntities.scala @@ -10,6 +10,7 @@ import cats.data.Kleisli import cats.effect._ import cats.implicits._ import fs2.Stream +import fs2.io.file.Files import docspell.analysis.TextAnalyser import docspell.analysis.classifier.TextClassifier.Data @@ -18,7 +19,7 @@ import docspell.scheduler._ import docspell.store.Store object LearnItemEntities { - def learnAll[F[_]: Async, A]( + def learnAll[F[_]: Async: Files, A]( analyser: TextAnalyser[F], store: Store[F], collective: CollectiveId, @@ -32,7 +33,7 @@ object LearnItemEntities { .flatMap(_ => learnConcPerson(analyser, store, collective, maxItems, maxTextLen)) .flatMap(_ => learnConcEquip(analyser, store, collective, maxItems, maxTextLen)) - def learnCorrOrg[F[_]: Async, A]( + def learnCorrOrg[F[_]: Async: Files, A]( analyser: TextAnalyser[F], store: Store[F], collective: CollectiveId, @@ -44,7 +45,7 @@ object LearnItemEntities { _ => SelectItems.forCorrOrg(store, collective, maxItems, maxTextLen) ) - def learnCorrPerson[F[_]: Async, A]( + def learnCorrPerson[F[_]: Async: Files, A]( analyser: TextAnalyser[F], store: Store[F], collective: CollectiveId, @@ -56,7 +57,7 @@ object LearnItemEntities { _ => SelectItems.forCorrPerson(store, collective, maxItems, maxTextLen) ) - def learnConcPerson[F[_]: Async, A]( + def learnConcPerson[F[_]: Async: Files, A]( analyser: TextAnalyser[F], store: Store[F], collective: CollectiveId, @@ -68,7 +69,7 @@ object LearnItemEntities { _ => SelectItems.forConcPerson(store, collective, maxItems, maxTextLen) ) - def learnConcEquip[F[_]: Async, A]( + def learnConcEquip[F[_]: Async: Files, A]( analyser: TextAnalyser[F], store: Store[F], collective: CollectiveId, @@ -80,7 +81,7 @@ object LearnItemEntities { _ => SelectItems.forConcEquip(store, collective, maxItems, maxTextLen) ) - private def learn[F[_]: Async, A]( + private def learn[F[_]: Async: Files, A]( store: Store[F], analyser: TextAnalyser[F], collective: CollectiveId diff --git a/modules/joex/src/main/scala/docspell/joex/learn/LearnTags.scala b/modules/joex/src/main/scala/docspell/joex/learn/LearnTags.scala index 9745f2aa..55eecb90 100644 --- a/modules/joex/src/main/scala/docspell/joex/learn/LearnTags.scala +++ b/modules/joex/src/main/scala/docspell/joex/learn/LearnTags.scala @@ -9,6 +9,7 @@ package docspell.joex.learn import cats.data.Kleisli import cats.effect._ import cats.implicits._ +import fs2.io.file.Files import docspell.analysis.TextAnalyser import docspell.common._ @@ -18,7 +19,7 @@ import docspell.store.records.RClassifierSetting object LearnTags { - def learnTagCategory[F[_]: Async, A]( + def learnTagCategory[F[_]: Async: Files, A]( analyser: TextAnalyser[F], store: Store[F], collective: CollectiveId, @@ -43,7 +44,10 @@ object LearnTags { ) } - def learnAllTagCategories[F[_]: Async, A](analyser: TextAnalyser[F], store: Store[F])( + def learnAllTagCategories[F[_]: Async: Files, A]( + analyser: TextAnalyser[F], + store: Store[F] + )( collective: CollectiveId, maxItems: Int, maxTextLen: Int diff --git a/modules/joex/src/main/scala/docspell/joex/learn/StoreClassifierModel.scala b/modules/joex/src/main/scala/docspell/joex/learn/StoreClassifierModel.scala index b3ff3261..6879dcb5 100644 --- a/modules/joex/src/main/scala/docspell/joex/learn/StoreClassifierModel.scala +++ b/modules/joex/src/main/scala/docspell/joex/learn/StoreClassifierModel.scala @@ -18,7 +18,7 @@ import docspell.store.records.RClassifierModel object StoreClassifierModel { - def handleModel[F[_]: Async]( + def handleModel[F[_]: Async: Files]( store: Store[F], logger: Logger[F], collective: CollectiveId, diff --git a/modules/joex/src/main/scala/docspell/joex/multiupload/MultiUploadArchiveTask.scala b/modules/joex/src/main/scala/docspell/joex/multiupload/MultiUploadArchiveTask.scala index e1ff77a1..61a6537a 100644 --- a/modules/joex/src/main/scala/docspell/joex/multiupload/MultiUploadArchiveTask.scala +++ b/modules/joex/src/main/scala/docspell/joex/multiupload/MultiUploadArchiveTask.scala @@ -11,6 +11,7 @@ import cats.data.OptionT import cats.effect._ import cats.implicits._ import fs2.Stream +import fs2.io.file.Files import docspell.backend.JobFactory import docspell.common._ @@ -35,7 +36,10 @@ import docspell.store.Store object MultiUploadArchiveTask { type Args = ProcessItemArgs - def apply[F[_]: Async](store: Store[F], jobStore: JobStore[F]): Task[F, Args, Result] = + def apply[F[_]: Async: Files]( + store: Store[F], + jobStore: JobStore[F] + ): Task[F, Args, Result] = Task { ctx => ctx.args.files .traverse { file => @@ -104,7 +108,7 @@ object MultiUploadArchiveTask { .map(_.mimetype.matches(MimeType.zip)) .getOrElse(false) - private def extractZip[F[_]: Async]( + private def extractZip[F[_]: Async: Files]( store: Store[F], args: Args )(file: ProcessItemArgs.File): Stream[F, ProcessItemArgs] = diff --git a/modules/joex/src/main/scala/docspell/joex/pdfconv/PdfConvTask.scala b/modules/joex/src/main/scala/docspell/joex/pdfconv/PdfConvTask.scala index a1ce38fb..8fe5e62e 100644 --- a/modules/joex/src/main/scala/docspell/joex/pdfconv/PdfConvTask.scala +++ b/modules/joex/src/main/scala/docspell/joex/pdfconv/PdfConvTask.scala @@ -11,6 +11,7 @@ import cats.data.OptionT import cats.effect._ import cats.implicits._ import fs2.Stream +import fs2.io.file.Files import docspell.common._ import docspell.convert.ConversionResult @@ -35,9 +36,9 @@ object PdfConvTask { deriveEncoder[Args] } - val taskName = Ident.unsafe("pdf-files-migration") + val taskName: Ident = Ident.unsafe("pdf-files-migration") - def apply[F[_]: Async](cfg: Config, store: Store[F]): Task[F, Args, Unit] = + def apply[F[_]: Async: Files](cfg: Config, store: Store[F]): Task[F, Args, Unit] = Task { ctx => for { _ <- ctx.logger.info(s"Converting pdf file ${ctx.args} using ocrmypdf") @@ -89,7 +90,7 @@ object PdfConvTask { else none.pure[F] } - def convert[F[_]: Async]( + def convert[F[_]: Async: Files]( cfg: Config, ctx: Context[F, Args], store: Store[F], diff --git a/modules/joex/src/main/scala/docspell/joex/process/ConvertPdf.scala b/modules/joex/src/main/scala/docspell/joex/process/ConvertPdf.scala index 648d6b29..6066c9fa 100644 --- a/modules/joex/src/main/scala/docspell/joex/process/ConvertPdf.scala +++ b/modules/joex/src/main/scala/docspell/joex/process/ConvertPdf.scala @@ -11,6 +11,7 @@ import cats.data.{Kleisli, OptionT} import cats.effect._ import cats.implicits._ import fs2.Stream +import fs2.io.file.Files import docspell.common._ import docspell.convert.ConversionResult.Handler @@ -35,7 +36,7 @@ import docspell.store.records._ object ConvertPdf { type Args = ProcessItemArgs - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( cfg: ConvertConfig, store: Store[F], item: ItemData @@ -76,7 +77,7 @@ object ConvertPdf { .map(_.mimetype) .getOrElse(MimeType.octetStream) - def convertSafe[F[_]: Async]( + def convertSafe[F[_]: Async: Files]( cfg: ConvertConfig, sanitizeHtml: SanitizeHtml, ctx: Context[F, Args], diff --git a/modules/joex/src/main/scala/docspell/joex/process/ExtractArchive.scala b/modules/joex/src/main/scala/docspell/joex/process/ExtractArchive.scala index 410e31bb..8baa488a 100644 --- a/modules/joex/src/main/scala/docspell/joex/process/ExtractArchive.scala +++ b/modules/joex/src/main/scala/docspell/joex/process/ExtractArchive.scala @@ -14,6 +14,7 @@ import cats.implicits._ import cats.kernel.Monoid import cats.kernel.Order import fs2.Stream +import fs2.io.file.Files import docspell.common._ import docspell.common.util.Zip @@ -35,12 +36,12 @@ import emil.Mail object ExtractArchive { type Args = ProcessItemArgs - def apply[F[_]: Async](store: Store[F])( + def apply[F[_]: Async: Files](store: Store[F])( item: ItemData ): Task[F, Args, ItemData] = multiPass(store, item, None).map(_._2) - def multiPass[F[_]: Async]( + def multiPass[F[_]: Async: Files]( store: Store[F], item: ItemData, archive: Option[RAttachmentArchive] @@ -50,7 +51,7 @@ object ExtractArchive { else multiPass(store, t._2, t._1) } - def singlePass[F[_]: Async]( + def singlePass[F[_]: Async: Files]( store: Store[F], item: ItemData, archive: Option[RAttachmentArchive] @@ -91,7 +92,7 @@ object ExtractArchive { .map(_.mimetype) .getOrElse(MimeType.octetStream) - def extractSafe[F[_]: Async]( + def extractSafe[F[_]: Async: Files]( ctx: Context[F, Args], store: Store[F], archive: Option[RAttachmentArchive] @@ -137,7 +138,7 @@ object ExtractArchive { } yield extracted.copy(files = extracted.files.filter(_.id != ra.id)) } - def extractZip[F[_]: Async]( + def extractZip[F[_]: Async: Files]( ctx: Context[F, Args], store: Store[F], archive: Option[RAttachmentArchive] diff --git a/modules/joex/src/main/scala/docspell/joex/process/ItemHandler.scala b/modules/joex/src/main/scala/docspell/joex/process/ItemHandler.scala index b5fc216e..8e361a2b 100644 --- a/modules/joex/src/main/scala/docspell/joex/process/ItemHandler.scala +++ b/modules/joex/src/main/scala/docspell/joex/process/ItemHandler.scala @@ -10,6 +10,7 @@ import cats.data.OptionT import cats.effect._ import cats.implicits._ import fs2.Stream +import fs2.io.file.Files import docspell.analysis.TextAnalyser import docspell.backend.joex.AddonOps @@ -36,7 +37,7 @@ object ItemHandler { } ) - def newItem[F[_]: Async]( + def newItem[F[_]: Async: Files]( cfg: Config, store: Store[F], itemOps: OItem[F], @@ -82,7 +83,7 @@ object ItemHandler { def isLastRetry[F[_]]: Task[F, Args, Boolean] = Task(_.isLastRetry) - def safeProcess[F[_]: Async]( + def safeProcess[F[_]: Async: Files]( cfg: Config, store: Store[F], itemOps: OItem[F], diff --git a/modules/joex/src/main/scala/docspell/joex/process/ProcessItem.scala b/modules/joex/src/main/scala/docspell/joex/process/ProcessItem.scala index 836a8062..9b52bcb9 100644 --- a/modules/joex/src/main/scala/docspell/joex/process/ProcessItem.scala +++ b/modules/joex/src/main/scala/docspell/joex/process/ProcessItem.scala @@ -8,6 +8,7 @@ package docspell.joex.process import cats.effect._ import cats.implicits._ +import fs2.io.file.Files import docspell.addons.AddonTriggerType import docspell.analysis.TextAnalyser @@ -22,7 +23,7 @@ import docspell.store.Store object ProcessItem { - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( cfg: Config, itemOps: OItem[F], fts: FtsClient[F], @@ -40,7 +41,7 @@ object ProcessItem { .flatMap(RemoveEmptyItem(itemOps)) .flatMap(RunAddons(addonOps, store, AddonTriggerType.FinalProcessItem)) - def processAttachments[F[_]: Async]( + def processAttachments[F[_]: Async: Files]( cfg: Config, fts: FtsClient[F], analyser: TextAnalyser[F], @@ -49,7 +50,7 @@ object ProcessItem { )(item: ItemData): Task[F, ProcessItemArgs, ItemData] = processAttachments0[F](cfg, fts, analyser, regexNer, store, (30, 60, 90))(item) - def analysisOnly[F[_]: Async]( + def analysisOnly[F[_]: Async: Files]( cfg: Config, analyser: TextAnalyser[F], regexNer: RegexNerFile[F], @@ -61,7 +62,7 @@ object ProcessItem { .flatMap(CrossCheckProposals[F](store)) .flatMap(SaveProposals[F](store)) - private def processAttachments0[F[_]: Async]( + private def processAttachments0[F[_]: Async: Files]( cfg: Config, fts: FtsClient[F], analyser: TextAnalyser[F], diff --git a/modules/joex/src/main/scala/docspell/joex/process/ReProcessItem.scala b/modules/joex/src/main/scala/docspell/joex/process/ReProcessItem.scala index 6890e37d..58ade825 100644 --- a/modules/joex/src/main/scala/docspell/joex/process/ReProcessItem.scala +++ b/modules/joex/src/main/scala/docspell/joex/process/ReProcessItem.scala @@ -9,6 +9,7 @@ package docspell.joex.process import cats.data.OptionT import cats.effect._ import cats.implicits._ +import fs2.io.file.Files import docspell.addons.AddonTriggerType import docspell.analysis.TextAnalyser @@ -30,7 +31,7 @@ import docspell.store.records.RItem object ReProcessItem { type Args = ReProcessItemArgs - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( cfg: Config, fts: FtsClient[F], itemOps: OItem[F], @@ -106,7 +107,7 @@ object ReProcessItem { ) } - def processFiles[F[_]: Async]( + def processFiles[F[_]: Async: Files]( cfg: Config, fts: FtsClient[F], itemOps: OItem[F], @@ -162,7 +163,7 @@ object ReProcessItem { def isLastRetry[F[_]]: Task[F, Args, Boolean] = Task(_.isLastRetry) - def safeProcess[F[_]: Async]( + def safeProcess[F[_]: Async: Files]( cfg: Config, fts: FtsClient[F], itemOps: OItem[F], diff --git a/modules/joex/src/main/scala/docspell/joex/process/RunAddons.scala b/modules/joex/src/main/scala/docspell/joex/process/RunAddons.scala index b564d8b4..5af296f6 100644 --- a/modules/joex/src/main/scala/docspell/joex/process/RunAddons.scala +++ b/modules/joex/src/main/scala/docspell/joex/process/RunAddons.scala @@ -8,6 +8,7 @@ package docspell.joex.process import cats.effect._ import cats.syntax.all._ +import fs2.io.file.Files import docspell.addons.AddonTriggerType import docspell.backend.joex.AddonOps @@ -22,7 +23,7 @@ import docspell.store.Store object RunAddons { type Args = ProcessItemArgs - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( ops: AddonOps[F], store: Store[F], trigger: AddonTriggerType diff --git a/modules/joex/src/main/scala/docspell/joex/process/TextAnalysis.scala b/modules/joex/src/main/scala/docspell/joex/process/TextAnalysis.scala index 006a80ef..ee268949 100644 --- a/modules/joex/src/main/scala/docspell/joex/process/TextAnalysis.scala +++ b/modules/joex/src/main/scala/docspell/joex/process/TextAnalysis.scala @@ -9,6 +9,7 @@ package docspell.joex.process import cats.Traverse import cats.effect._ import cats.implicits._ +import fs2.io.file.Files import docspell.analysis.classifier.TextClassifier import docspell.analysis.{NlpSettings, TextAnalyser} @@ -26,7 +27,7 @@ import docspell.store.records.{RAttachmentMeta, RClassifierSetting} object TextAnalysis { type Args = ProcessItemArgs - def apply[F[_]: Async]( + def apply[F[_]: Async: Files]( cfg: Config.TextAnalysis, analyser: TextAnalyser[F], nerFile: RegexNerFile[F], @@ -87,7 +88,7 @@ object TextAnalysis { } yield (rm.copy(nerlabels = labels.all.toList), AttachmentDates(rm, labels.dates)) } - def predictTags[F[_]: Async]( + def predictTags[F[_]: Async: Files]( ctx: Context[F, Args], store: Store[F], cfg: Config.TextAnalysis, @@ -107,7 +108,7 @@ object TextAnalysis { } yield tags.flatten } - def predictItemEntities[F[_]: Async]( + def predictItemEntities[F[_]: Async: Files]( ctx: Context[F, Args], store: Store[F], cfg: Config.TextAnalysis, @@ -139,7 +140,7 @@ object TextAnalysis { .map(MetaProposalList.apply) } - private def makeClassify[F[_]: Async]( + private def makeClassify[F[_]: Async: Files]( ctx: Context[F, Args], store: Store[F], cfg: Config.TextAnalysis, diff --git a/modules/joex/src/main/scala/docspell/joex/process/TextExtraction.scala b/modules/joex/src/main/scala/docspell/joex/process/TextExtraction.scala index 420e1d38..df00a3e2 100644 --- a/modules/joex/src/main/scala/docspell/joex/process/TextExtraction.scala +++ b/modules/joex/src/main/scala/docspell/joex/process/TextExtraction.scala @@ -9,6 +9,7 @@ package docspell.joex.process import cats.data.OptionT import cats.effect._ import cats.implicits._ +import fs2.io.file.Files import docspell.common._ import docspell.extract.{ExtractConfig, ExtractResult, Extraction} @@ -19,7 +20,7 @@ import docspell.store.records.{RAttachment, RAttachmentMeta, RFileMeta} object TextExtraction { - def apply[F[_]: Async](cfg: ExtractConfig, fts: FtsClient[F], store: Store[F])( + def apply[F[_]: Async: Files](cfg: ExtractConfig, fts: FtsClient[F], store: Store[F])( item: ItemData ): Task[F, ProcessItemArgs, ItemData] = Task { ctx => @@ -66,7 +67,7 @@ object TextExtraction { case class Result(am: RAttachmentMeta, td: TextData, tags: List[String] = Nil) - def extractTextIfEmpty[F[_]: Async]( + def extractTextIfEmpty[F[_]: Async: Files]( ctx: Context[F, ProcessItemArgs], store: Store[F], cfg: ExtractConfig, @@ -100,7 +101,7 @@ object TextExtraction { } } - def extractTextToMeta[F[_]: Async]( + def extractTextToMeta[F[_]: Async: Files]( ctx: Context[F, _], store: Store[F], cfg: ExtractConfig, @@ -143,7 +144,7 @@ object TextExtraction { .flatMap(mt => extr.extractText(data, DataType(mt), lang)) } - private def extractTextFallback[F[_]: Async]( + private def extractTextFallback[F[_]: Async: Files]( ctx: Context[F, _], store: Store[F], cfg: ExtractConfig, diff --git a/modules/joexapi/src/main/scala/docspell/joexapi/client/JoexClient.scala b/modules/joexapi/src/main/scala/docspell/joexapi/client/JoexClient.scala index 1674235c..6ce9ed72 100644 --- a/modules/joexapi/src/main/scala/docspell/joexapi/client/JoexClient.scala +++ b/modules/joexapi/src/main/scala/docspell/joexapi/client/JoexClient.scala @@ -8,6 +8,7 @@ package docspell.joexapi.client import cats.effect._ import cats.implicits._ +import fs2.io.net.Network import docspell.common.{Ident, LenientUri} import docspell.joexapi.model.{AddonSupport, BasicResult} @@ -72,6 +73,6 @@ object JoexClient { Uri.unsafeFromString(u.asString) } - def resource[F[_]: Async]: Resource[F, JoexClient[F]] = + def resource[F[_]: Async: Network]: Resource[F, JoexClient[F]] = EmberClientBuilder.default[F].build.map(apply[F]) } diff --git a/modules/restserver/src/main/scala/docspell/restserver/ConfigFile.scala b/modules/restserver/src/main/scala/docspell/restserver/ConfigFile.scala index 58d3b9ca..e7132fd6 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/ConfigFile.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/ConfigFile.scala @@ -10,6 +10,7 @@ import java.security.SecureRandom import cats.Monoid import cats.effect.Async +import fs2.io.file.Files import docspell.backend.auth.Login import docspell.backend.signup.{Config => SignupConfig} @@ -30,7 +31,7 @@ object ConfigFile { // IntelliJ is wrong, this is required import Implicits._ - def loadConfig[F[_]: Async](args: List[String]): F[Config] = { + def loadConfig[F[_]: Async: Files](args: List[String]): F[Config] = { val logger = docspell.logging.getLogger[F] val validate = Validation.of( diff --git a/modules/restserver/src/main/scala/docspell/restserver/RestAppImpl.scala b/modules/restserver/src/main/scala/docspell/restserver/RestAppImpl.scala index 513dceb4..d09be4fb 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/RestAppImpl.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/RestAppImpl.scala @@ -9,6 +9,7 @@ package docspell.restserver import cats.effect._ import fs2.Stream import fs2.concurrent.Topic +import fs2.io.file.Files import docspell.backend.BackendApp import docspell.backend.auth.{AuthToken, ShareToken} @@ -36,7 +37,7 @@ import org.http4s.client.Client import org.http4s.server.Router import org.http4s.server.websocket.WebSocketBuilder2 -final class RestAppImpl[F[_]: Async]( +final class RestAppImpl[F[_]: Async: Files]( val config: Config, val backend: BackendApp[F], httpClient: Client[F], @@ -162,7 +163,7 @@ final class RestAppImpl[F[_]: Async]( object RestAppImpl { - def create[F[_]: Async]( + def create[F[_]: Async: Files]( cfg: Config, pools: Pools, store: Store[F], diff --git a/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala b/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala index 3b3a8755..400d5d27 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala @@ -12,6 +12,8 @@ import cats.effect._ import cats.implicits._ import fs2.Stream import fs2.concurrent.Topic +import fs2.io.file.Files +import fs2.io.net.Network import docspell.backend.msg.Topics import docspell.backend.ops.ONode @@ -35,7 +37,7 @@ import org.http4s.server.websocket.WebSocketBuilder2 object RestServer { - def serve[F[_]: Async]( + def serve[F[_]: Async: Files: Network]( cfg: Config, pools: Pools ): F[ExitCode] = @@ -55,7 +57,7 @@ object RestServer { .flatMap { case (restApp, pubSub, setting) => Stream( restApp.subscriptions, - restApp.eventConsume(2), + restApp.eventConsume(maxConcurrent = 2), Stream.resource { if (cfg.serverOptions.enableHttp2) EmberServerBuilder @@ -81,7 +83,7 @@ object RestServer { (server ++ Stream(keepAlive)).parJoinUnbounded.compile.drain.as(ExitCode.Success) } yield exit - def createApp[F[_]: Async]( + def createApp[F[_]: Async: Files: Network]( cfg: Config, pools: Pools, wsTopic: Topic[F, OutputEvent] diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/UploadRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/UploadRoutes.scala index 20bd3484..492e0ee5 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/UploadRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/UploadRoutes.scala @@ -9,6 +9,7 @@ package docspell.restserver.routes import cats.data.OptionT import cats.effect._ import cats.implicits._ +import fs2.io.file.Files import docspell.backend.BackendApp import docspell.backend.auth.AuthToken @@ -25,7 +26,7 @@ import org.log4s._ object UploadRoutes { private[this] val logger = getLogger - def secured[F[_]: Async]( + def secured[F[_]: Async: Files]( backend: BackendApp[F], cfg: Config, user: AuthToken @@ -50,7 +51,7 @@ object UploadRoutes { } } - def open[F[_]: Async](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = { + def open[F[_]: Async: Files](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = { val dsl = new Http4sDsl[F] with ResponseGenerator[F] {} import dsl._ @@ -78,7 +79,7 @@ object UploadRoutes { } } - private def submitFiles[F[_]: Async]( + private def submitFiles[F[_]: Async: Files]( backend: BackendApp[F], cfg: Config, accOrSrc: Either[Ident, CollectiveId], diff --git a/project/Dependencies.scala b/project/Dependencies.scala index daed5e41..6819c8db 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -20,9 +20,9 @@ object Dependencies { val EmilVersion = "0.13.0" val FlexmarkVersion = "0.64.8" val FlywayVersion = "9.22.3" - val Fs2Version = "3.6.1" + val Fs2Version = "3.9.2" val H2Version = "2.2.224" - val Http4sVersion = "0.23.18" + val Http4sVersion = "0.23.23" val Icu4jVersion = "74.1" val JavaOtpVersion = "0.4.0" val JsoupVersion = "1.16.2"