mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-02 21:42:52 +00:00
commit
75679cd681
@ -6,7 +6,9 @@
|
|||||||
|
|
||||||
package docspell.backend.ops
|
package docspell.backend.ops
|
||||||
|
|
||||||
|
import cats.Applicative
|
||||||
import cats.effect._
|
import cats.effect._
|
||||||
|
import cats.implicits._
|
||||||
|
|
||||||
import docspell.backend.msg.{CancelJob, Topics}
|
import docspell.backend.msg.{CancelJob, Topics}
|
||||||
import docspell.common.Ident
|
import docspell.common.Ident
|
||||||
@ -20,13 +22,13 @@ trait OJoex[F[_]] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object OJoex {
|
object OJoex {
|
||||||
def apply[F[_]](pubSub: PubSubT[F]): Resource[F, OJoex[F]] =
|
def apply[F[_]: Applicative](pubSub: PubSubT[F]): Resource[F, OJoex[F]] =
|
||||||
Resource.pure[F, OJoex[F]](new OJoex[F] {
|
Resource.pure[F, OJoex[F]](new OJoex[F] {
|
||||||
|
|
||||||
def notifyAllNodes: F[Unit] =
|
def notifyAllNodes: F[Unit] =
|
||||||
pubSub.publish1IgnoreErrors(Topics.jobsNotify, ())
|
pubSub.publish1IgnoreErrors(Topics.jobsNotify, ()).as(())
|
||||||
|
|
||||||
def cancelJob(job: Ident, worker: Ident): F[Unit] =
|
def cancelJob(job: Ident, worker: Ident): F[Unit] =
|
||||||
pubSub.publish1IgnoreErrors(CancelJob.topic, CancelJob(job, worker))
|
pubSub.publish1IgnoreErrors(CancelJob.topic, CancelJob(job, worker)).as(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ import docspell.joex.process.ReProcessItem
|
|||||||
import docspell.joex.scanmailbox._
|
import docspell.joex.scanmailbox._
|
||||||
import docspell.joex.scheduler._
|
import docspell.joex.scheduler._
|
||||||
import docspell.joex.updatecheck._
|
import docspell.joex.updatecheck._
|
||||||
import docspell.joexapi.client.JoexClient
|
|
||||||
import docspell.pubsub.api.{PubSub, PubSubT}
|
import docspell.pubsub.api.{PubSub, PubSubT}
|
||||||
import docspell.store.Store
|
import docspell.store.Store
|
||||||
import docspell.store.queue._
|
import docspell.store.queue._
|
||||||
@ -127,7 +126,6 @@ object JoexAppImpl {
|
|||||||
): Resource[F, JoexApp[F]] =
|
): Resource[F, JoexApp[F]] =
|
||||||
for {
|
for {
|
||||||
pstore <- PeriodicTaskStore.create(store)
|
pstore <- PeriodicTaskStore.create(store)
|
||||||
client = JoexClient(httpClient)
|
|
||||||
pubSubT = PubSubT(
|
pubSubT = PubSubT(
|
||||||
pubSub,
|
pubSub,
|
||||||
Logger.log4s(org.log4s.getLogger(s"joex-${cfg.appId.id}"))
|
Logger.log4s(org.log4s.getLogger(s"joex-${cfg.appId.id}"))
|
||||||
@ -271,7 +269,7 @@ object JoexAppImpl {
|
|||||||
sch,
|
sch,
|
||||||
queue,
|
queue,
|
||||||
pstore,
|
pstore,
|
||||||
client
|
joex
|
||||||
)
|
)
|
||||||
app = new JoexAppImpl(cfg, store, queue, pubSubT, pstore, termSignal, sch, psch)
|
app = new JoexAppImpl(cfg, store, queue, pubSubT, pstore, termSignal, sch, psch)
|
||||||
appR <- Resource.make(app.init.map(_ => app))(_.initShutdown)
|
appR <- Resource.make(app.init.map(_ => app))(_.initShutdown)
|
||||||
|
@ -10,7 +10,7 @@ import cats.effect._
|
|||||||
import fs2._
|
import fs2._
|
||||||
import fs2.concurrent.SignallingRef
|
import fs2.concurrent.SignallingRef
|
||||||
|
|
||||||
import docspell.joexapi.client.JoexClient
|
import docspell.backend.ops.OJoex
|
||||||
import docspell.store.queue._
|
import docspell.store.queue._
|
||||||
|
|
||||||
/** A periodic scheduler takes care to submit periodic tasks to the job queue.
|
/** A periodic scheduler takes care to submit periodic tasks to the job queue.
|
||||||
@ -40,7 +40,7 @@ object PeriodicScheduler {
|
|||||||
sch: Scheduler[F],
|
sch: Scheduler[F],
|
||||||
queue: JobQueue[F],
|
queue: JobQueue[F],
|
||||||
store: PeriodicTaskStore[F],
|
store: PeriodicTaskStore[F],
|
||||||
client: JoexClient[F]
|
joex: OJoex[F]
|
||||||
): Resource[F, PeriodicScheduler[F]] =
|
): Resource[F, PeriodicScheduler[F]] =
|
||||||
for {
|
for {
|
||||||
waiter <- Resource.eval(SignallingRef(true))
|
waiter <- Resource.eval(SignallingRef(true))
|
||||||
@ -50,7 +50,7 @@ object PeriodicScheduler {
|
|||||||
sch,
|
sch,
|
||||||
queue,
|
queue,
|
||||||
store,
|
store,
|
||||||
client,
|
joex,
|
||||||
waiter,
|
waiter,
|
||||||
state
|
state
|
||||||
)
|
)
|
||||||
|
@ -11,10 +11,10 @@ import cats.implicits._
|
|||||||
import fs2._
|
import fs2._
|
||||||
import fs2.concurrent.SignallingRef
|
import fs2.concurrent.SignallingRef
|
||||||
|
|
||||||
|
import docspell.backend.ops.OJoex
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
import docspell.common.syntax.all._
|
import docspell.common.syntax.all._
|
||||||
import docspell.joex.scheduler.PeriodicSchedulerImpl.State
|
import docspell.joex.scheduler.PeriodicSchedulerImpl.State
|
||||||
import docspell.joexapi.client.JoexClient
|
|
||||||
import docspell.store.queue._
|
import docspell.store.queue._
|
||||||
import docspell.store.records.RPeriodicTask
|
import docspell.store.records.RPeriodicTask
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ final class PeriodicSchedulerImpl[F[_]: Async](
|
|||||||
sch: Scheduler[F],
|
sch: Scheduler[F],
|
||||||
queue: JobQueue[F],
|
queue: JobQueue[F],
|
||||||
store: PeriodicTaskStore[F],
|
store: PeriodicTaskStore[F],
|
||||||
client: JoexClient[F],
|
joex: OJoex[F],
|
||||||
waiter: SignallingRef[F, Boolean],
|
waiter: SignallingRef[F, Boolean],
|
||||||
state: SignallingRef[F, State[F]]
|
state: SignallingRef[F, State[F]]
|
||||||
) extends PeriodicScheduler[F] {
|
) extends PeriodicScheduler[F] {
|
||||||
@ -119,9 +119,7 @@ final class PeriodicSchedulerImpl[F[_]: Async](
|
|||||||
}
|
}
|
||||||
|
|
||||||
def notifyJoex: F[Unit] =
|
def notifyJoex: F[Unit] =
|
||||||
sch.notifyChange *> store.findJoexNodes.flatMap(
|
sch.notifyChange *> joex.notifyAllNodes
|
||||||
_.traverse(n => client.notifyJoexIgnoreErrors(n.url)).map(_ => ())
|
|
||||||
)
|
|
||||||
|
|
||||||
def scheduleNotify(pj: RPeriodicTask): F[Unit] =
|
def scheduleNotify(pj: RPeriodicTask): F[Unit] =
|
||||||
Timestamp
|
Timestamp
|
||||||
|
@ -15,7 +15,7 @@ import docspell.common.{Ident, Timestamp}
|
|||||||
import io.circe.Json
|
import io.circe.Json
|
||||||
|
|
||||||
trait PubSub[F[_]] {
|
trait PubSub[F[_]] {
|
||||||
def publish1(topic: Topic, msg: Json): F[MessageHead]
|
def publish1(topic: Topic, msg: Json): F[F[MessageHead]]
|
||||||
|
|
||||||
def publish(topic: Topic): Pipe[F, Json, MessageHead]
|
def publish(topic: Topic): Pipe[F, Json, MessageHead]
|
||||||
|
|
||||||
@ -24,8 +24,10 @@ trait PubSub[F[_]] {
|
|||||||
object PubSub {
|
object PubSub {
|
||||||
def noop[F[_]: Applicative]: PubSub[F] =
|
def noop[F[_]: Applicative]: PubSub[F] =
|
||||||
new PubSub[F] {
|
new PubSub[F] {
|
||||||
def publish1(topic: Topic, msg: Json): F[MessageHead] =
|
def publish1(topic: Topic, msg: Json): F[F[MessageHead]] =
|
||||||
Applicative[F].pure(MessageHead(Ident.unsafe("0"), Timestamp.Epoch, topic))
|
Applicative[F].pure(
|
||||||
|
Applicative[F].pure(MessageHead(Ident.unsafe("0"), Timestamp.Epoch, topic))
|
||||||
|
)
|
||||||
|
|
||||||
def publish(topic: Topic): Pipe[F, Json, MessageHead] =
|
def publish(topic: Topic): Pipe[F, Json, MessageHead] =
|
||||||
_ => Stream.empty
|
_ => Stream.empty
|
||||||
|
@ -16,9 +16,9 @@ import docspell.common.Logger
|
|||||||
|
|
||||||
trait PubSubT[F[_]] {
|
trait PubSubT[F[_]] {
|
||||||
|
|
||||||
def publish1[A](topic: TypedTopic[A], msg: A): F[MessageHead]
|
def publish1[A](topic: TypedTopic[A], msg: A): F[F[MessageHead]]
|
||||||
|
|
||||||
def publish1IgnoreErrors[A](topic: TypedTopic[A], msg: A): F[Unit]
|
def publish1IgnoreErrors[A](topic: TypedTopic[A], msg: A): F[F[Unit]]
|
||||||
|
|
||||||
def publish[A](topic: TypedTopic[A]): Pipe[F, A, MessageHead]
|
def publish[A](topic: TypedTopic[A]): Pipe[F, A, MessageHead]
|
||||||
|
|
||||||
@ -37,15 +37,15 @@ object PubSubT {
|
|||||||
|
|
||||||
def apply[F[_]: Async](pubSub: PubSub[F], logger: Logger[F]): PubSubT[F] =
|
def apply[F[_]: Async](pubSub: PubSub[F], logger: Logger[F]): PubSubT[F] =
|
||||||
new PubSubT[F] {
|
new PubSubT[F] {
|
||||||
def publish1[A](topic: TypedTopic[A], msg: A): F[MessageHead] =
|
def publish1[A](topic: TypedTopic[A], msg: A): F[F[MessageHead]] =
|
||||||
pubSub.publish1(topic.topic, topic.codec(msg))
|
pubSub.publish1(topic.topic, topic.codec(msg))
|
||||||
|
|
||||||
def publish1IgnoreErrors[A](topic: TypedTopic[A], msg: A): F[Unit] =
|
def publish1IgnoreErrors[A](topic: TypedTopic[A], msg: A): F[F[Unit]] =
|
||||||
publish1(topic, msg).attempt.flatMap {
|
publish1(topic, msg).map(_.attempt.flatMap {
|
||||||
case Right(_) => ().pure[F]
|
case Right(_) => ().pure[F]
|
||||||
case Left(ex) =>
|
case Left(ex) =>
|
||||||
logger.error(ex)(s"Error publishing to topic ${topic.topic.name}: $msg")
|
logger.error(ex)(s"Error publishing to topic ${topic.topic.name}: $msg")
|
||||||
}
|
})
|
||||||
|
|
||||||
def publish[A](topic: TypedTopic[A]): Pipe[F, A, MessageHead] =
|
def publish[A](topic: TypedTopic[A]): Pipe[F, A, MessageHead] =
|
||||||
_.map(topic.codec.apply).through(pubSub.publish(topic.topic))
|
_.map(topic.codec.apply).through(pubSub.publish(topic.topic))
|
||||||
|
@ -65,7 +65,10 @@ final class NaivePubSub[F[_]: Async](
|
|||||||
def withClient(client: Client[F]): NaivePubSub[F] =
|
def withClient(client: Client[F]): NaivePubSub[F] =
|
||||||
new NaivePubSub[F](cfg, state, store, client)
|
new NaivePubSub[F](cfg, state, store, client)
|
||||||
|
|
||||||
def publish1(topic: Topic, msgBody: Json): F[MessageHead] =
|
def publish1(topic: Topic, msgBody: Json): F[F[MessageHead]] =
|
||||||
|
Async[F].start(publish0(topic, msgBody)).map(fiber => fiber.joinWithNever)
|
||||||
|
|
||||||
|
def publish0(topic: Topic, msgBody: Json): F[MessageHead] =
|
||||||
for {
|
for {
|
||||||
head <- mkMessageHead(topic)
|
head <- mkMessageHead(topic)
|
||||||
msg = Message(head, msgBody)
|
msg = Message(head, msgBody)
|
||||||
@ -78,7 +81,7 @@ final class NaivePubSub[F[_]: Async](
|
|||||||
|
|
||||||
def publish(topic: Topic): Pipe[F, Json, MessageHead] =
|
def publish(topic: Topic): Pipe[F, Json, MessageHead] =
|
||||||
ms => //TODO Do some optimization by grouping messages to the same topic
|
ms => //TODO Do some optimization by grouping messages to the same topic
|
||||||
ms.evalMap(publish1(topic, _))
|
ms.evalMap(publish0(topic, _))
|
||||||
|
|
||||||
def subscribe(topics: NonEmptyList[Topic]): Stream[F, Message[Json]] =
|
def subscribe(topics: NonEmptyList[Topic]): Stream[F, Message[Json]] =
|
||||||
(for {
|
(for {
|
||||||
|
@ -44,7 +44,7 @@ class NaivePubSubTest extends CatsEffectSuite with Fixtures {
|
|||||||
for {
|
for {
|
||||||
res <- subscribe(ps, Topics.jobSubmitted)
|
res <- subscribe(ps, Topics.jobSubmitted)
|
||||||
(received, _, subFiber) = res
|
(received, _, subFiber) = res
|
||||||
headSend <- ps.publish1(Topics.jobSubmitted, JobSubmittedMsg("hello".id))
|
headSend <- ps.publish1(Topics.jobSubmitted, JobSubmittedMsg("hello".id)).flatten
|
||||||
outcome <- subFiber.join
|
outcome <- subFiber.join
|
||||||
msgRec <- received.get
|
msgRec <- received.get
|
||||||
_ = assert(outcome.isSuccess)
|
_ = assert(outcome.isSuccess)
|
||||||
|
@ -24,6 +24,7 @@ type alias Texts =
|
|||||||
, authFailed : String
|
, authFailed : String
|
||||||
, fulltextPlaceholder : String
|
, fulltextPlaceholder : String
|
||||||
, powerSearchPlaceholder : String
|
, powerSearchPlaceholder : String
|
||||||
|
, normalSearchPlaceholder : String
|
||||||
, extendedSearch : String
|
, extendedSearch : String
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,6 +40,7 @@ gb =
|
|||||||
, fulltextPlaceholder = "Fulltext search…"
|
, fulltextPlaceholder = "Fulltext search…"
|
||||||
, powerSearchPlaceholder = "Extended search…"
|
, powerSearchPlaceholder = "Extended search…"
|
||||||
, extendedSearch = "Extended search query"
|
, extendedSearch = "Extended search query"
|
||||||
|
, normalSearchPlaceholder = "Search…"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -53,4 +55,5 @@ de =
|
|||||||
, fulltextPlaceholder = "Volltextsuche…"
|
, fulltextPlaceholder = "Volltextsuche…"
|
||||||
, powerSearchPlaceholder = "Erweiterte Suche…"
|
, powerSearchPlaceholder = "Erweiterte Suche…"
|
||||||
, extendedSearch = "Erweiterte Suchanfrage"
|
, extendedSearch = "Erweiterte Suchanfrage"
|
||||||
|
, normalSearchPlaceholder = "Suche…"
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ module Page.Share.Menubar exposing (view)
|
|||||||
import Comp.Basic as B
|
import Comp.Basic as B
|
||||||
import Comp.MenuBar as MB
|
import Comp.MenuBar as MB
|
||||||
import Comp.PowerSearchInput
|
import Comp.PowerSearchInput
|
||||||
import Comp.SearchMenu
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput)
|
import Html.Events exposing (onClick, onInput)
|
||||||
@ -20,16 +20,9 @@ import Styles as S
|
|||||||
import Util.Html
|
import Util.Html
|
||||||
|
|
||||||
|
|
||||||
view : Texts -> Model -> Html Msg
|
view : Texts -> Flags -> Model -> Html Msg
|
||||||
view texts model =
|
view texts flags model =
|
||||||
let
|
let
|
||||||
btnStyle =
|
|
||||||
S.secondaryBasicButton ++ " text-sm"
|
|
||||||
|
|
||||||
searchInput =
|
|
||||||
Comp.SearchMenu.textSearchString
|
|
||||||
model.searchMenuModel.textSearchModel
|
|
||||||
|
|
||||||
powerSearchBar =
|
powerSearchBar =
|
||||||
div [ class "flex-grow flex flex-col relative" ]
|
div [ class "flex-grow flex flex-col relative" ]
|
||||||
[ div
|
[ div
|
||||||
@ -67,7 +60,11 @@ view texts model =
|
|||||||
[ type_ "text"
|
[ type_ "text"
|
||||||
, class S.textInput
|
, class S.textInput
|
||||||
, class "text-sm"
|
, class "text-sm"
|
||||||
, placeholder texts.fulltextPlaceholder
|
, if flags.config.fullTextSearchEnabled then
|
||||||
|
placeholder texts.fulltextPlaceholder
|
||||||
|
|
||||||
|
else
|
||||||
|
placeholder texts.normalSearchPlaceholder
|
||||||
, onInput SetContentSearch
|
, onInput SetContentSearch
|
||||||
, value (Maybe.withDefault "" model.contentSearch)
|
, value (Maybe.withDefault "" model.contentSearch)
|
||||||
, Util.Html.onKeyUpCode ContentSearchKey
|
, Util.Html.onKeyUpCode ContentSearchKey
|
||||||
|
@ -214,7 +214,11 @@ makeSearchCmd flags model =
|
|||||||
model.powerSearchInput.input
|
model.powerSearchInput.input
|
||||||
|
|
||||||
SearchBarContent ->
|
SearchBarContent ->
|
||||||
Maybe.map (Q.Contents >> Q.render) model.contentSearch
|
if flags.config.fullTextSearchEnabled then
|
||||||
|
Maybe.map (Q.Contents >> Q.render) model.contentSearch
|
||||||
|
|
||||||
|
else
|
||||||
|
Maybe.map (Q.AllNames >> Q.render) model.contentSearch
|
||||||
]
|
]
|
||||||
|
|
||||||
request mq =
|
request mq =
|
||||||
|
@ -80,7 +80,7 @@ mainContent texts flags settings shareId model =
|
|||||||
]
|
]
|
||||||
[ text <| Maybe.withDefault "" model.verifyResult.name
|
[ text <| Maybe.withDefault "" model.verifyResult.name
|
||||||
]
|
]
|
||||||
, Menubar.view texts model
|
, Menubar.view texts flags model
|
||||||
, errorMessage texts model
|
, errorMessage texts model
|
||||||
, Results.view texts settings flags shareId model
|
, Results.view texts settings flags shareId model
|
||||||
]
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user