mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-04-05 02:49:32 +00:00
Initial impl of search route
This commit is contained in:
parent
f4596db63d
commit
a286556116
@ -12,7 +12,7 @@ import cats.implicits._
|
||||
|
||||
import docspell.backend.PasswordCrypt
|
||||
import docspell.backend.auth.ShareToken
|
||||
import docspell.backend.ops.OShare.VerifyResult
|
||||
import docspell.backend.ops.OShare.{ShareQuery, VerifyResult}
|
||||
import docspell.common._
|
||||
import docspell.query.ItemQuery
|
||||
import docspell.store.Store
|
||||
@ -36,26 +36,37 @@ trait OShare[F[_]] {
|
||||
removePassword: Boolean
|
||||
): F[OShare.ChangeResult]
|
||||
|
||||
// ---
|
||||
|
||||
/** Verifies the given id and password and returns a authorization token on success. */
|
||||
def verify(key: ByteVector)(id: Ident, password: Option[Password]): F[VerifyResult]
|
||||
|
||||
/** Verifies the authorization token. */
|
||||
def verifyToken(key: ByteVector)(token: String): F[VerifyResult]
|
||||
|
||||
def findShareQuery(id: Ident): OptionT[F, ShareQuery]
|
||||
}
|
||||
|
||||
object OShare {
|
||||
final case class ShareQuery(id: Ident, cid: Ident, query: ItemQuery)
|
||||
|
||||
sealed trait VerifyResult {
|
||||
def toEither: Either[String, ShareToken] =
|
||||
this match {
|
||||
case VerifyResult.Success(token) => Right(token)
|
||||
case _ => Left("Authentication failed.")
|
||||
case VerifyResult.Success(token, _) =>
|
||||
Right(token)
|
||||
case _ => Left("Authentication failed.")
|
||||
}
|
||||
}
|
||||
object VerifyResult {
|
||||
case class Success(token: ShareToken) extends VerifyResult
|
||||
case class Success(token: ShareToken, shareName: Option[String]) extends VerifyResult
|
||||
case object NotFound extends VerifyResult
|
||||
case object PasswordMismatch extends VerifyResult
|
||||
case object InvalidToken extends VerifyResult
|
||||
|
||||
def success(token: ShareToken): VerifyResult = Success(token)
|
||||
def success(token: ShareToken): VerifyResult = Success(token, None)
|
||||
def success(token: ShareToken, name: Option[String]): VerifyResult =
|
||||
Success(token, name)
|
||||
def notFound: VerifyResult = NotFound
|
||||
def passwordMismatch: VerifyResult = PasswordMismatch
|
||||
def invalidToken: VerifyResult = InvalidToken
|
||||
@ -158,8 +169,8 @@ object OShare {
|
||||
|
||||
val token = ShareToken.create(id, shareKey)
|
||||
pwCheck match {
|
||||
case Some(true) => token.map(VerifyResult.success)
|
||||
case None => token.map(VerifyResult.success)
|
||||
case Some(true) => token.map(t => VerifyResult.success(t, share.name))
|
||||
case None => token.map(t => VerifyResult.success(t, share.name))
|
||||
case Some(false) => VerifyResult.passwordMismatch.pure[F]
|
||||
}
|
||||
}
|
||||
@ -186,5 +197,11 @@ object OShare {
|
||||
logger.debug(s"Invalid session token: $err") *>
|
||||
VerifyResult.invalidToken.pure[F]
|
||||
}
|
||||
|
||||
def findShareQuery(id: Ident): OptionT[F, ShareQuery] =
|
||||
RShare
|
||||
.findCurrentActive(id)
|
||||
.mapK(store.transform)
|
||||
.map(share => ShareQuery(share.id, share.cid, share.query))
|
||||
}
|
||||
}
|
||||
|
@ -1558,6 +1558,30 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BasicResult"
|
||||
|
||||
/share/search:
|
||||
post:
|
||||
operationId: "share-search"
|
||||
tags: [Share]
|
||||
summary: Performs a search in a share.
|
||||
description: |
|
||||
Allows to run a search query in the shared documents. The
|
||||
input data structure is the same as with a standard query. The
|
||||
`searchMode` parameter is ignored here.
|
||||
security:
|
||||
- shareTokenHeader: []
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ItemQuery"
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ItemLightList"
|
||||
|
||||
/admin/user/resetPassword:
|
||||
post:
|
||||
operationId: "admin-user-reset-password"
|
||||
@ -4248,6 +4272,11 @@ components:
|
||||
type: boolean
|
||||
message:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
description: |
|
||||
The name of the share if it exists. Only valid to use when
|
||||
`success` is `true`.
|
||||
|
||||
ShareData:
|
||||
description: |
|
||||
@ -6475,6 +6504,10 @@ components:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: Docspell-Admin-Secret
|
||||
shareTokenHeader:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: Docspell-Share-Auth
|
||||
parameters:
|
||||
id:
|
||||
name: id
|
||||
|
@ -81,16 +81,16 @@ object ShareRoutes {
|
||||
res <- backend.share
|
||||
.verify(cfg.auth.serverSecret)(secret.shareId, secret.password)
|
||||
resp <- res match {
|
||||
case VerifyResult.Success(token) =>
|
||||
case VerifyResult.Success(token, name) =>
|
||||
val cd = ShareCookieData(token)
|
||||
Ok(ShareVerifyResult(true, token.asString, false, "Success"))
|
||||
Ok(ShareVerifyResult(true, token.asString, false, "Success", name))
|
||||
.map(cd.addCookie(ClientRequestInfo.getBaseUrl(cfg, req)))
|
||||
case VerifyResult.PasswordMismatch =>
|
||||
Ok(ShareVerifyResult(false, "", true, "Failed"))
|
||||
Ok(ShareVerifyResult(false, "", true, "Failed", None))
|
||||
case VerifyResult.NotFound =>
|
||||
Ok(ShareVerifyResult(false, "", false, "Failed"))
|
||||
Ok(ShareVerifyResult(false, "", false, "Failed", None))
|
||||
case VerifyResult.InvalidToken =>
|
||||
Ok(ShareVerifyResult(false, "", false, "Failed"))
|
||||
Ok(ShareVerifyResult(false, "", false, "Failed", None))
|
||||
}
|
||||
} yield resp
|
||||
}
|
||||
|
@ -7,13 +7,20 @@
|
||||
package docspell.restserver.routes
|
||||
|
||||
import cats.effect._
|
||||
import cats.implicits._
|
||||
|
||||
import docspell.backend.BackendApp
|
||||
import docspell.backend.auth.ShareToken
|
||||
import docspell.common.Logger
|
||||
import docspell.backend.ops.OSimpleSearch
|
||||
import docspell.common._
|
||||
import docspell.restapi.model.ItemQuery
|
||||
import docspell.restserver.Config
|
||||
import docspell.store.qb.Batch
|
||||
import docspell.store.queries.Query
|
||||
|
||||
import org.http4s.HttpRoutes
|
||||
import org.http4s.circe.CirceEntityDecoder._
|
||||
import org.http4s.dsl.Http4sDsl
|
||||
|
||||
object ShareSearchRoutes {
|
||||
|
||||
@ -23,7 +30,37 @@ object ShareSearchRoutes {
|
||||
token: ShareToken
|
||||
): HttpRoutes[F] = {
|
||||
val logger = Logger.log4s[F](org.log4s.getLogger)
|
||||
logger.trace(s"$backend $cfg $token")
|
||||
???
|
||||
|
||||
val dsl = new Http4sDsl[F] {}
|
||||
import dsl._
|
||||
|
||||
HttpRoutes.of { case req @ POST -> Root =>
|
||||
backend.share
|
||||
.findShareQuery(token.id)
|
||||
.semiflatMap { share =>
|
||||
for {
|
||||
userQuery <- req.as[ItemQuery]
|
||||
batch = Batch(
|
||||
userQuery.offset.getOrElse(0),
|
||||
userQuery.limit.getOrElse(cfg.maxItemPageSize)
|
||||
).restrictLimitTo(
|
||||
cfg.maxItemPageSize
|
||||
)
|
||||
itemQuery = ItemQueryString(userQuery.query)
|
||||
settings = OSimpleSearch.Settings(
|
||||
batch,
|
||||
cfg.fullTextSearch.enabled,
|
||||
userQuery.withDetails.getOrElse(false),
|
||||
cfg.maxNoteLength,
|
||||
searchMode = SearchMode.Normal
|
||||
)
|
||||
account = AccountId(share.cid, Ident.unsafe(""))
|
||||
fixQuery = Query.Fix(account, Some(share.query.expr), None)
|
||||
_ <- logger.debug(s"Searching in share ${share.id.id}: ${userQuery.query}")
|
||||
resp <- ItemRoutes.searchItems(backend, dsl)(settings, fixQuery, itemQuery)
|
||||
} yield resp
|
||||
}
|
||||
.getOrElseF(NotFound())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user