mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-21 09:58:26 +00:00
@ -2,6 +2,7 @@ version = "3.0.6"
|
|||||||
|
|
||||||
preset = default
|
preset = default
|
||||||
align.preset = some
|
align.preset = some
|
||||||
|
runner.dialect = scala213
|
||||||
|
|
||||||
maxColumn = 90
|
maxColumn = 90
|
||||||
|
|
||||||
|
32
build.sbt
32
build.sbt
@ -293,10 +293,23 @@ val common = project
|
|||||||
Dependencies.circe ++
|
Dependencies.circe ++
|
||||||
Dependencies.loggingApi ++
|
Dependencies.loggingApi ++
|
||||||
Dependencies.calevCore ++
|
Dependencies.calevCore ++
|
||||||
Dependencies.calevCirce ++
|
Dependencies.calevCirce
|
||||||
Dependencies.pureconfig.map(_ % "optional")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val config = project
|
||||||
|
.in(file("modules/config"))
|
||||||
|
.disablePlugins(RevolverPlugin)
|
||||||
|
.settings(sharedSettings)
|
||||||
|
.settings(testSettingsMUnit)
|
||||||
|
.settings(
|
||||||
|
name := "docspell-config",
|
||||||
|
addCompilerPlugin(Dependencies.kindProjectorPlugin),
|
||||||
|
libraryDependencies ++=
|
||||||
|
Dependencies.fs2 ++
|
||||||
|
Dependencies.pureconfig
|
||||||
|
)
|
||||||
|
.dependsOn(common)
|
||||||
|
|
||||||
// Some example files for testing
|
// Some example files for testing
|
||||||
// https://file-examples.com/index.php/sample-documents-download/sample-doc-download/
|
// https://file-examples.com/index.php/sample-documents-download/sample-doc-download/
|
||||||
val files = project
|
val files = project
|
||||||
@ -603,7 +616,17 @@ val joex = project
|
|||||||
),
|
),
|
||||||
Revolver.enableDebugging(port = 5051, suspend = false)
|
Revolver.enableDebugging(port = 5051, suspend = false)
|
||||||
)
|
)
|
||||||
.dependsOn(store, backend, extract, convert, analysis, joexapi, restapi, ftssolr)
|
.dependsOn(
|
||||||
|
config,
|
||||||
|
store,
|
||||||
|
backend,
|
||||||
|
extract,
|
||||||
|
convert,
|
||||||
|
analysis,
|
||||||
|
joexapi,
|
||||||
|
restapi,
|
||||||
|
ftssolr
|
||||||
|
)
|
||||||
|
|
||||||
val restserver = project
|
val restserver = project
|
||||||
.in(file("modules/restserver"))
|
.in(file("modules/restserver"))
|
||||||
@ -666,7 +689,7 @@ val restserver = project
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.dependsOn(restapi, joexapi, backend, webapp, ftssolr, oidc)
|
.dependsOn(config, restapi, joexapi, backend, webapp, ftssolr, oidc)
|
||||||
|
|
||||||
// --- Website Documentation
|
// --- Website Documentation
|
||||||
|
|
||||||
@ -731,6 +754,7 @@ val root = project
|
|||||||
)
|
)
|
||||||
.aggregate(
|
.aggregate(
|
||||||
common,
|
common,
|
||||||
|
config,
|
||||||
extract,
|
extract,
|
||||||
convert,
|
convert,
|
||||||
analysis,
|
analysis,
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Eike K. & Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package docspell.config
|
||||||
|
|
||||||
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
|
import cats.data.OptionT
|
||||||
|
import cats.effect._
|
||||||
|
import cats.implicits._
|
||||||
|
import fs2.io.file.{Files, Path}
|
||||||
|
|
||||||
|
import docspell.common.Logger
|
||||||
|
|
||||||
|
import pureconfig.{ConfigReader, ConfigSource}
|
||||||
|
|
||||||
|
object ConfigFactory {
|
||||||
|
|
||||||
|
/** Reads the configuration trying the following in order:
|
||||||
|
* 1. if 'args' contains at least one element, the first is interpreted as a config
|
||||||
|
* file
|
||||||
|
* 1. otherwise check the system property 'config.file' for an existing file and use
|
||||||
|
* it if it does exist; ignore if it doesn't exist
|
||||||
|
* 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)(
|
||||||
|
args: List[String]
|
||||||
|
): F[C] =
|
||||||
|
findFileFromArgs(args).flatMap {
|
||||||
|
case Some(file) =>
|
||||||
|
logger.info(s"Using config file: $file") *>
|
||||||
|
readFile[F, C](file, atPath)
|
||||||
|
case None =>
|
||||||
|
checkSystemProperty.value.flatMap {
|
||||||
|
case Some(file) =>
|
||||||
|
logger.info(s"Using config file from system property: $file") *>
|
||||||
|
readConfig(atPath)
|
||||||
|
case None =>
|
||||||
|
logger.info("Using config from environment variables!") *>
|
||||||
|
readEnv(atPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reads the configuration from the given file. */
|
||||||
|
private def readFile[F[_]: Sync, C: ClassTag: ConfigReader](
|
||||||
|
file: Path,
|
||||||
|
at: String
|
||||||
|
): F[C] =
|
||||||
|
Sync[F].delay {
|
||||||
|
System.setProperty(
|
||||||
|
"config.file",
|
||||||
|
file.toNioPath.toAbsolutePath.normalize.toString
|
||||||
|
)
|
||||||
|
ConfigSource.default.at(at).loadOrThrow[C]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reads the config as specified in typesafe's config library; usually loading the file
|
||||||
|
* given as system property 'config.file'.
|
||||||
|
*/
|
||||||
|
private def readConfig[F[_]: Sync, C: ClassTag: ConfigReader](
|
||||||
|
at: String
|
||||||
|
): F[C] =
|
||||||
|
Sync[F].delay(ConfigSource.default.at(at).loadOrThrow[C])
|
||||||
|
|
||||||
|
/** Reads the configuration from environment variables. */
|
||||||
|
private def readEnv[F[_]: Sync, C: ClassTag: ConfigReader](at: String): F[C] =
|
||||||
|
Sync[F].delay(ConfigSource.fromConfig(EnvConfig.get).at(at).loadOrThrow[C])
|
||||||
|
|
||||||
|
/** 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]] =
|
||||||
|
args.headOption
|
||||||
|
.map(Path.apply)
|
||||||
|
.traverse(p =>
|
||||||
|
Files[F].exists(p).flatMap {
|
||||||
|
case true => p.pure[F]
|
||||||
|
case false => Async[F].raiseError(new Exception(s"File not found: $p"))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
/** If the system property 'config.file' is set, it is checked whether the file exists.
|
||||||
|
* If it doesn't exist, the property is removed to not raise any exception. In contrast
|
||||||
|
* 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] =
|
||||||
|
for {
|
||||||
|
cf <- OptionT(
|
||||||
|
Sync[F].delay(
|
||||||
|
Option(System.getProperty("config.file")).map(_.trim).filter(_.nonEmpty)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cp = Path(cf)
|
||||||
|
exists <- OptionT.liftF(Files[F].exists(cp))
|
||||||
|
file <-
|
||||||
|
if (exists) OptionT.pure[F](cp)
|
||||||
|
else
|
||||||
|
OptionT
|
||||||
|
.liftF(Sync[F].delay(System.clearProperty("config.file")))
|
||||||
|
.flatMap(_ => OptionT.none[F, Path])
|
||||||
|
} yield file
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Eike K. & Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package docspell.config
|
||||||
|
|
||||||
|
import java.util.Properties
|
||||||
|
|
||||||
|
import scala.collection.{MapView, mutable}
|
||||||
|
import scala.jdk.CollectionConverters._
|
||||||
|
|
||||||
|
import com.typesafe.config.{Config, ConfigFactory}
|
||||||
|
|
||||||
|
/** Creates a config from environment variables.
|
||||||
|
*
|
||||||
|
* The env variables are expected to be in same form as the config keys with the
|
||||||
|
* following mangling: a dot is replaced by an underscore character, because this is the
|
||||||
|
* standard separator for env variables. In order to represent dashes, two underscores
|
||||||
|
* are needed (and for one underscore use three underscores in the env variable).
|
||||||
|
*
|
||||||
|
* For example, the config key
|
||||||
|
* {{{
|
||||||
|
* docspell.server.app-name
|
||||||
|
* }}}
|
||||||
|
* can be given as env variable
|
||||||
|
* {{{
|
||||||
|
* DOCSPELL_SERVER_APP__NAME
|
||||||
|
* }}}
|
||||||
|
*/
|
||||||
|
object EnvConfig {
|
||||||
|
|
||||||
|
/** The config from current environment. */
|
||||||
|
lazy val get: Config =
|
||||||
|
loadFrom(System.getenv().asScala.view)
|
||||||
|
|
||||||
|
def loadFrom(env: MapView[String, String]): Config = {
|
||||||
|
val cfg = new Properties()
|
||||||
|
for (key <- env.keySet if key.startsWith("DOCSPELL_"))
|
||||||
|
cfg.setProperty(envToProp(key), env(key))
|
||||||
|
|
||||||
|
ConfigFactory
|
||||||
|
.parseProperties(cfg)
|
||||||
|
.withFallback(ConfigFactory.defaultReference())
|
||||||
|
.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Docspell has all lowercase key names and uses snake case.
|
||||||
|
*
|
||||||
|
* So this converts to lowercase and then replaces underscores (like
|
||||||
|
* [[com.typesafe.config.ConfigFactory.systemEnvironmentOverrides()]]
|
||||||
|
*
|
||||||
|
* - 3 underscores -> `_` (underscore)
|
||||||
|
* - 2 underscores -> `-` (dash)
|
||||||
|
* - 1 underscore -> `.` (dot)
|
||||||
|
*/
|
||||||
|
private[config] def envToProp(v: String): String = {
|
||||||
|
val len = v.length
|
||||||
|
val buffer = new mutable.StringBuilder()
|
||||||
|
val underscoreMapping = Map(3 -> '_', 2 -> '-', 1 -> '.').withDefault(_ => '_')
|
||||||
|
@annotation.tailrec
|
||||||
|
def go(current: Int, underscores: Int): String =
|
||||||
|
if (current >= len) buffer.toString()
|
||||||
|
else
|
||||||
|
v.charAt(current) match {
|
||||||
|
case '_' => go(current + 1, underscores + 1)
|
||||||
|
case c =>
|
||||||
|
if (underscores > 0) {
|
||||||
|
buffer.append(underscoreMapping(underscores))
|
||||||
|
}
|
||||||
|
buffer.append(c.toLower)
|
||||||
|
go(current + 1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
go(0, 0)
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package docspell.common.config
|
package docspell.config
|
||||||
|
|
||||||
import java.nio.file.{Path => JPath}
|
import java.nio.file.{Path => JPath}
|
||||||
|
|
||||||
@ -15,12 +15,11 @@ import fs2.io.file.Path
|
|||||||
import docspell.common._
|
import docspell.common._
|
||||||
|
|
||||||
import com.github.eikek.calev.CalEvent
|
import com.github.eikek.calev.CalEvent
|
||||||
import pureconfig._
|
import pureconfig.ConfigReader
|
||||||
import pureconfig.error.{CannotConvert, FailureReason}
|
import pureconfig.error.{CannotConvert, FailureReason}
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
object Implicits {
|
object Implicits {
|
||||||
|
|
||||||
implicit val accountIdReader: ConfigReader[AccountId] =
|
implicit val accountIdReader: ConfigReader[AccountId] =
|
||||||
ConfigReader[String].emap(reason(AccountId.parse))
|
ConfigReader[String].emap(reason(AccountId.parse))
|
||||||
|
|
5
modules/config/src/test/resources/reference.conf
Normal file
5
modules/config/src/test/resources/reference.conf
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
docspell.server {
|
||||||
|
bind {
|
||||||
|
port = 7880
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Eike K. & Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package docspell.config
|
||||||
|
|
||||||
|
import munit.FunSuite
|
||||||
|
|
||||||
|
class EnvConfigTest extends FunSuite {
|
||||||
|
|
||||||
|
test("convert underscores") {
|
||||||
|
assertEquals(EnvConfig.envToProp("A_B_C"), "a.b.c")
|
||||||
|
assertEquals(EnvConfig.envToProp("A_B__C"), "a.b-c")
|
||||||
|
assertEquals(EnvConfig.envToProp("AA_BB__CC___D"), "aa.bb-cc_d")
|
||||||
|
}
|
||||||
|
|
||||||
|
test("insert docspell keys") {
|
||||||
|
val cfg = EnvConfig.loadFrom(
|
||||||
|
Map(
|
||||||
|
"DOCSPELL_SERVER_APP__NAME" -> "Hello!",
|
||||||
|
"DOCSPELL_JOEX_BIND_PORT" -> "1234"
|
||||||
|
).view
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(cfg.getString("docspell.server.app-name"), "Hello!")
|
||||||
|
assertEquals(cfg.getInt("docspell.joex.bind.port"), 1234)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("find default values from reference.conf") {
|
||||||
|
val cfg = EnvConfig.loadFrom(
|
||||||
|
Map(
|
||||||
|
"DOCSPELL_SERVER_APP__NAME" -> "Hello!",
|
||||||
|
"DOCSPELL_JOEX_BIND_PORT" -> "1234"
|
||||||
|
).view
|
||||||
|
)
|
||||||
|
assertEquals(cfg.getInt("docspell.server.bind.port"), 7880)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("discard non docspell keys") {
|
||||||
|
val cfg = EnvConfig.loadFrom(Map("A_B_C" -> "12").view)
|
||||||
|
assert(!cfg.hasPath("a.b.c"))
|
||||||
|
}
|
||||||
|
}
|
@ -8,9 +8,12 @@ package docspell.joex
|
|||||||
|
|
||||||
import cats.data.Validated
|
import cats.data.Validated
|
||||||
import cats.data.ValidatedNec
|
import cats.data.ValidatedNec
|
||||||
|
import cats.effect.Async
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
|
||||||
import docspell.common.config.Implicits._
|
import docspell.common.Logger
|
||||||
|
import docspell.config.ConfigFactory
|
||||||
|
import docspell.config.Implicits._
|
||||||
import docspell.joex.scheduler.CountingScheme
|
import docspell.joex.scheduler.CountingScheme
|
||||||
|
|
||||||
import emil.MailAddress
|
import emil.MailAddress
|
||||||
@ -22,8 +25,12 @@ import yamusca.imports._
|
|||||||
object ConfigFile {
|
object ConfigFile {
|
||||||
import Implicits._
|
import Implicits._
|
||||||
|
|
||||||
def loadConfig: Config =
|
def loadConfig[F[_]: Async](args: List[String]): F[Config] = {
|
||||||
validOrThrow(ConfigSource.default.at("docspell.joex").loadOrThrow[Config])
|
val logger = Logger.log4s[F](org.log4s.getLogger)
|
||||||
|
ConfigFactory
|
||||||
|
.default[F, Config](logger, "docspell.joex")(args)
|
||||||
|
.map(cfg => validOrThrow(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
private def validOrThrow(cfg: Config): Config =
|
private def validOrThrow(cfg: Config): Config =
|
||||||
validate(cfg).fold(err => sys.error(err.toList.mkString("- ", "\n", "")), identity)
|
validate(cfg).fold(err => sys.error(err.toList.mkString("- ", "\n", "")), identity)
|
||||||
|
@ -6,46 +6,23 @@
|
|||||||
|
|
||||||
package docspell.joex
|
package docspell.joex
|
||||||
|
|
||||||
import java.nio.file.{Files, Paths}
|
|
||||||
|
|
||||||
import cats.effect._
|
import cats.effect._
|
||||||
import cats.implicits._
|
|
||||||
|
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
|
|
||||||
import org.log4s._
|
import org.log4s.getLogger
|
||||||
|
|
||||||
object Main extends IOApp {
|
object Main extends IOApp {
|
||||||
private[this] val logger = getLogger
|
|
||||||
|
|
||||||
val blockingEC =
|
private val logger: Logger[IO] = Logger.log4s[IO](getLogger)
|
||||||
ThreadFactories.cached[IO](ThreadFactories.ofName("docspell-joex-blocking"))
|
|
||||||
val connectEC =
|
private val connectEC =
|
||||||
ThreadFactories.fixed[IO](5, ThreadFactories.ofName("docspell-joex-dbconnect"))
|
ThreadFactories.fixed[IO](5, ThreadFactories.ofName("docspell-joex-dbconnect"))
|
||||||
val restserverEC =
|
|
||||||
ThreadFactories.workSteal[IO](ThreadFactories.ofNameFJ("docspell-joex-server"))
|
|
||||||
|
|
||||||
def run(args: List[String]) = {
|
def run(args: List[String]): IO[ExitCode] =
|
||||||
args match {
|
for {
|
||||||
case file :: Nil =>
|
cfg <- ConfigFile.loadConfig[IO](args)
|
||||||
val path = Paths.get(file).toAbsolutePath.normalize
|
banner = Banner(
|
||||||
logger.info(s"Using given config file: $path")
|
|
||||||
System.setProperty("config.file", file)
|
|
||||||
case _ =>
|
|
||||||
Option(System.getProperty("config.file")) match {
|
|
||||||
case Some(f) if f.nonEmpty =>
|
|
||||||
val path = Paths.get(f).toAbsolutePath.normalize
|
|
||||||
if (!Files.exists(path)) {
|
|
||||||
logger.info(s"Not using config file '$f' because it doesn't exist")
|
|
||||||
System.clearProperty("config.file")
|
|
||||||
} else
|
|
||||||
logger.info(s"Using config file from system properties: $f")
|
|
||||||
case _ =>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val cfg = ConfigFile.loadConfig
|
|
||||||
val banner = Banner(
|
|
||||||
"JOEX",
|
"JOEX",
|
||||||
BuildInfo.version,
|
BuildInfo.version,
|
||||||
BuildInfo.gitHeadCommit,
|
BuildInfo.gitHeadCommit,
|
||||||
@ -55,18 +32,19 @@ object Main extends IOApp {
|
|||||||
cfg.baseUrl,
|
cfg.baseUrl,
|
||||||
Some(cfg.fullTextSearch.solr.url).filter(_ => cfg.fullTextSearch.enabled)
|
Some(cfg.fullTextSearch.solr.url).filter(_ => cfg.fullTextSearch.enabled)
|
||||||
)
|
)
|
||||||
logger.info(s"\n${banner.render("***>")}")
|
_ <- logger.info(s"\n${banner.render("***>")}")
|
||||||
|
_ <-
|
||||||
if (EnvMode.current.isDev) {
|
if (EnvMode.current.isDev) {
|
||||||
logger.warn(">>>>> Docspell is running in DEV mode! <<<<<")
|
logger.warn(">>>>> Docspell is running in DEV mode! <<<<<")
|
||||||
}
|
} else IO(())
|
||||||
|
|
||||||
val pools = connectEC.map(Pools.apply)
|
pools = connectEC.map(Pools.apply)
|
||||||
pools.use(p =>
|
rc <- pools.use(p =>
|
||||||
JoexServer
|
JoexServer
|
||||||
.stream[IO](cfg, p)
|
.stream[IO](cfg, p)
|
||||||
.compile
|
.compile
|
||||||
.drain
|
.drain
|
||||||
.as(ExitCode.Success)
|
.as(ExitCode.Success)
|
||||||
)
|
)
|
||||||
}
|
} yield rc
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,13 @@ package docspell.restserver
|
|||||||
|
|
||||||
import cats.Semigroup
|
import cats.Semigroup
|
||||||
import cats.data.{Validated, ValidatedNec}
|
import cats.data.{Validated, ValidatedNec}
|
||||||
|
import cats.effect.Async
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
|
||||||
import docspell.backend.signup.{Config => SignupConfig}
|
import docspell.backend.signup.{Config => SignupConfig}
|
||||||
import docspell.common.config.Implicits._
|
import docspell.common.Logger
|
||||||
|
import docspell.config.ConfigFactory
|
||||||
|
import docspell.config.Implicits._
|
||||||
import docspell.oidc.{ProviderConfig, SignatureAlgo}
|
import docspell.oidc.{ProviderConfig, SignatureAlgo}
|
||||||
import docspell.restserver.auth.OpenId
|
import docspell.restserver.auth.OpenId
|
||||||
|
|
||||||
@ -21,8 +24,12 @@ import pureconfig.generic.auto._
|
|||||||
object ConfigFile {
|
object ConfigFile {
|
||||||
import Implicits._
|
import Implicits._
|
||||||
|
|
||||||
def loadConfig: Config =
|
def loadConfig[F[_]: Async](args: List[String]): F[Config] = {
|
||||||
Validate(ConfigSource.default.at("docspell.server").loadOrThrow[Config])
|
val logger = Logger.log4s(org.log4s.getLogger)
|
||||||
|
ConfigFactory
|
||||||
|
.default[F, Config](logger, "docspell.server")(args)
|
||||||
|
.map(cfg => Validate(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
object Implicits {
|
object Implicits {
|
||||||
implicit val signupModeReader: ConfigReader[SignupConfig.Mode] =
|
implicit val signupModeReader: ConfigReader[SignupConfig.Mode] =
|
||||||
|
@ -6,46 +6,21 @@
|
|||||||
|
|
||||||
package docspell.restserver
|
package docspell.restserver
|
||||||
|
|
||||||
import java.nio.file.{Files, Paths}
|
|
||||||
|
|
||||||
import cats.effect._
|
import cats.effect._
|
||||||
import cats.implicits._
|
|
||||||
|
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
|
|
||||||
import org.log4s._
|
import org.log4s.getLogger
|
||||||
|
|
||||||
object Main extends IOApp {
|
object Main extends IOApp {
|
||||||
private[this] val logger = getLogger
|
private[this] val logger: Logger[IO] = Logger.log4s(getLogger)
|
||||||
|
|
||||||
val blockingEC =
|
private val connectEC =
|
||||||
ThreadFactories.cached[IO](ThreadFactories.ofName("docspell-restserver-blocking"))
|
|
||||||
val connectEC =
|
|
||||||
ThreadFactories.fixed[IO](5, ThreadFactories.ofName("docspell-dbconnect"))
|
ThreadFactories.fixed[IO](5, ThreadFactories.ofName("docspell-dbconnect"))
|
||||||
val restserverEC =
|
|
||||||
ThreadFactories.workSteal[IO](ThreadFactories.ofNameFJ("docspell-restserver"))
|
|
||||||
|
|
||||||
def run(args: List[String]) = {
|
def run(args: List[String]) = for {
|
||||||
args match {
|
cfg <- ConfigFile.loadConfig[IO](args)
|
||||||
case file :: Nil =>
|
banner = Banner(
|
||||||
val path = Paths.get(file).toAbsolutePath.normalize
|
|
||||||
logger.info(s"Using given config file: $path")
|
|
||||||
System.setProperty("config.file", file)
|
|
||||||
case _ =>
|
|
||||||
Option(System.getProperty("config.file")) match {
|
|
||||||
case Some(f) if f.nonEmpty =>
|
|
||||||
val path = Paths.get(f).toAbsolutePath.normalize
|
|
||||||
if (!Files.exists(path)) {
|
|
||||||
logger.info(s"Not using config file '$f' because it doesn't exist")
|
|
||||||
System.clearProperty("config.file")
|
|
||||||
} else
|
|
||||||
logger.info(s"Using config file from system properties: $f")
|
|
||||||
case _ =>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val cfg = ConfigFile.loadConfig
|
|
||||||
val banner = Banner(
|
|
||||||
"REST Server",
|
"REST Server",
|
||||||
BuildInfo.version,
|
BuildInfo.version,
|
||||||
BuildInfo.gitHeadCommit,
|
BuildInfo.gitHeadCommit,
|
||||||
@ -55,12 +30,14 @@ object Main extends IOApp {
|
|||||||
cfg.baseUrl,
|
cfg.baseUrl,
|
||||||
Some(cfg.fullTextSearch.solr.url).filter(_ => cfg.fullTextSearch.enabled)
|
Some(cfg.fullTextSearch.solr.url).filter(_ => cfg.fullTextSearch.enabled)
|
||||||
)
|
)
|
||||||
val pools = connectEC.map(Pools.apply)
|
_ <- logger.info(s"\n${banner.render("***>")}")
|
||||||
logger.info(s"\n${banner.render("***>")}")
|
_ <-
|
||||||
if (EnvMode.current.isDev) {
|
if (EnvMode.current.isDev) {
|
||||||
logger.warn(">>>>> Docspell is running in DEV mode! <<<<<")
|
logger.warn(">>>>> Docspell is running in DEV mode! <<<<<")
|
||||||
}
|
} else IO(())
|
||||||
|
|
||||||
|
pools = connectEC.map(Pools.apply)
|
||||||
|
rc <-
|
||||||
pools.use(p =>
|
pools.use(p =>
|
||||||
RestServer
|
RestServer
|
||||||
.stream[IO](cfg, p)
|
.stream[IO](cfg, p)
|
||||||
@ -68,5 +45,5 @@ object Main extends IOApp {
|
|||||||
.drain
|
.drain
|
||||||
.as(ExitCode.Success)
|
.as(ExitCode.Success)
|
||||||
)
|
)
|
||||||
}
|
} yield rc
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user