Upgrade code base to CE3

This commit is contained in:
eikek
2021-06-21 21:33:54 +02:00
parent 903ec26e54
commit bd791b4593
146 changed files with 638 additions and 758 deletions

View File

@ -1,12 +1,12 @@
package docspell.restserver
import java.net.InetAddress
import docspell.backend.auth.Login
import docspell.backend.{Config => BackendConfig}
import docspell.common._
import docspell.ftssolr.SolrConfig
import com.comcast.ip4s.IpAddress
case class Config(
appName: String,
appId: Ident,
@ -42,12 +42,14 @@ object Config {
case class HttpHeader(enabled: Boolean, headerName: String, headerValue: String)
case class AllowedIps(enabled: Boolean, ips: Set[String]) {
def containsAddress(inet: InetAddress): Boolean = {
val ip = inet.getHostAddress
def containsAddress(inet: IpAddress): Boolean = {
val ip = inet.fold(_.toUriString, _.toUriString) //.getHostAddress
lazy val ipParts = ip.split('.')
def checkSingle(pattern: String): Boolean =
pattern == ip || (inet.isLoopbackAddress && pattern == "127.0.0.1") || (pattern
pattern == ip || (ip.contains(
"localhost"
) && pattern == "127.0.0.1") || (pattern
.split('.')
.zip(ipParts)
.foldLeft(true) { case (r, (a, b)) =>

View File

@ -52,9 +52,8 @@ object Main extends IOApp {
val pools = for {
cec <- connectEC
bec <- blockingEC
blocker = Blocker.liftExecutorService(bec)
rec <- restserverEC
} yield Pools(cec, bec, blocker, rec)
} yield Pools(cec, bec, rec)
logger.info(s"\n${banner.render("***>")}")
if (EnvMode.current.isDev) {

View File

@ -24,21 +24,20 @@ final class RestAppImpl[F[_]](val config: Config, val backend: BackendApp[F])
object RestAppImpl {
def create[F[_]: ConcurrentEffect: ContextShift](
def create[F[_]: Async](
cfg: Config,
connectEC: ExecutionContext,
httpClientEc: ExecutionContext,
blocker: Blocker
httpClientEc: ExecutionContext
): Resource[F, RestApp[F]] =
for {
backend <- BackendApp(cfg.backend, connectEC, httpClientEc, blocker)(
backend <- BackendApp(cfg.backend, connectEC, httpClientEc)(
createFtsClient[F](cfg)
)
app = new RestAppImpl[F](cfg, backend)
appR <- Resource.make(app.init.map(_ => app))(_.shutdown)
} yield appR
private def createFtsClient[F[_]: ConcurrentEffect](
private def createFtsClient[F[_]: Async](
cfg: Config
)(client: Client[F]): Resource[F, FtsClient[F]] =
if (cfg.fullTextSearch.enabled) SolrFtsClient(cfg.fullTextSearch.solr, client)

View File

@ -11,36 +11,33 @@ import docspell.restserver.routes._
import docspell.restserver.webapp._
import org.http4s._
import org.http4s.blaze.server.BlazeServerBuilder
import org.http4s.dsl.Http4sDsl
import org.http4s.headers.Location
import org.http4s.implicits._
import org.http4s.server.Router
import org.http4s.server.blaze.BlazeServerBuilder
import org.http4s.server.middleware.Logger
object RestServer {
def stream[F[_]: ConcurrentEffect](
cfg: Config,
pools: Pools
)(implicit T: Timer[F], CS: ContextShift[F]): Stream[F, Nothing] = {
def stream[F[_]: Async](cfg: Config, pools: Pools): Stream[F, Nothing] = {
val templates = TemplateRoutes[F](pools.blocker, cfg)
val templates = TemplateRoutes[F](cfg)
val app = for {
restApp <-
RestAppImpl
.create[F](cfg, pools.connectEC, pools.httpClientEC, pools.blocker)
.create[F](cfg, pools.connectEC, pools.httpClientEC)
httpApp = Router(
"/api/info" -> routes.InfoRoutes(),
"/api/v1/open/" -> openRoutes(cfg, restApp),
"/api/v1/sec/" -> Authenticate(restApp.backend.login, cfg.auth) { token =>
securedRoutes(cfg, pools, restApp, token)
securedRoutes(cfg, restApp, token)
},
"/api/v1/admin" -> AdminRoutes(cfg.adminEndpoint) {
adminRoutes(cfg, restApp)
},
"/api/doc" -> templates.doc,
"/app/assets" -> EnvMiddleware(WebjarRoutes.appRoutes[F](pools.blocker)),
"/app/assets" -> EnvMiddleware(WebjarRoutes.appRoutes[F]),
"/app" -> EnvMiddleware(templates.app),
"/sw.js" -> EnvMiddleware(templates.serviceWorker),
"/" -> redirectTo("/app")
@ -61,9 +58,8 @@ object RestServer {
)
}.drain
def securedRoutes[F[_]: Effect: ContextShift](
def securedRoutes[F[_]: Async](
cfg: Config,
pools: Pools,
restApp: RestApp[F],
token: AuthToken
): HttpRoutes[F] =
@ -77,9 +73,9 @@ object RestServer {
"user" -> UserRoutes(restApp.backend, token),
"collective" -> CollectiveRoutes(restApp.backend, token),
"queue" -> JobQueueRoutes(restApp.backend, token),
"item" -> ItemRoutes(cfg, pools.blocker, restApp.backend, token),
"item" -> ItemRoutes(cfg, restApp.backend, token),
"items" -> ItemMultiRoutes(restApp.backend, token),
"attachment" -> AttachmentRoutes(pools.blocker, restApp.backend, token),
"attachment" -> AttachmentRoutes(restApp.backend, token),
"attachments" -> AttachmentMultiRoutes(restApp.backend, token),
"upload" -> UploadRoutes.secured(restApp.backend, cfg, token),
"checkfile" -> CheckFileRoutes.secured(restApp.backend, token),
@ -95,7 +91,7 @@ object RestServer {
"clientSettings" -> ClientSettingsRoutes(restApp.backend, token)
)
def openRoutes[F[_]: Effect](cfg: Config, restApp: RestApp[F]): HttpRoutes[F] =
def openRoutes[F[_]: Async](cfg: Config, restApp: RestApp[F]): HttpRoutes[F] =
Router(
"auth" -> LoginRoutes.login(restApp.backend.login, cfg),
"signup" -> RegisterRoutes(restApp.backend, cfg),
@ -104,14 +100,14 @@ object RestServer {
"integration" -> IntegrationEndpointRoutes.open(restApp.backend, cfg)
)
def adminRoutes[F[_]: Effect](cfg: Config, restApp: RestApp[F]): HttpRoutes[F] =
def adminRoutes[F[_]: Async](cfg: Config, restApp: RestApp[F]): HttpRoutes[F] =
Router(
"fts" -> FullTextIndexRoutes.admin(cfg, restApp.backend),
"user" -> UserRoutes.admin(restApp.backend),
"info" -> InfoRoutes.admin(cfg)
)
def redirectTo[F[_]: Effect](path: String): HttpRoutes[F] = {
def redirectTo[F[_]: Async](path: String): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
@ -119,7 +115,7 @@ object RestServer {
Response[F](
Status.SeeOther,
body = Stream.empty,
headers = Headers.of(Location(Uri(path = path)))
headers = Headers(Location(Uri(path = Uri.Path.unsafeFromString(path))))
).pure[F]
}
}

View File

@ -5,7 +5,7 @@ import docspell.common.AccountId
import docspell.common.LenientUri
import org.http4s._
import org.http4s.util._
import org.typelevel.ci.CIString
case class CookieData(auth: AuthToken) {
def accountId: AccountId = auth.account
@ -37,7 +37,7 @@ object CookieData {
def fromCookie[F[_]](req: Request[F]): Either[String, String] =
for {
header <- headers.Cookie.from(req.headers).toRight("Cookie parsing error")
header <- req.headers.get[headers.Cookie].toRight("Cookie parsing error")
cookie <-
header.values.toList
.find(_.name == cookieName)
@ -46,8 +46,8 @@ object CookieData {
def fromHeader[F[_]](req: Request[F]): Either[String, String] =
req.headers
.get(CaseInsensitiveString(headerName))
.map(_.value)
.get(CIString(headerName))
.map(_.head.value)
.toRight("Couldn't find an authenticator")
def deleteCookie(baseUrl: LenientUri): ResponseCookie =

View File

@ -33,7 +33,7 @@ object RememberCookieData {
def fromCookie[F[_]](req: Request[F]): Option[String] =
for {
header <- headers.Cookie.from(req.headers)
header <- req.headers.get[headers.Cookie]
cookie <- header.values.toList.find(_.name == cookieName)
} yield cookie.content

View File

@ -2,7 +2,7 @@ package docspell.restserver.conv
import java.time.{LocalDate, ZoneId}
import cats.effect.{Effect, Sync}
import cats.effect.{Async, Sync}
import cats.implicits._
import fs2.Stream
@ -294,7 +294,7 @@ trait Conversions {
JobLogEvent(jl.created, jl.level, jl.message)
// upload
def readMultipart[F[_]: Effect](
def readMultipart[F[_]: Async](
mp: Multipart[F],
sourceName: String,
logger: Logger,
@ -347,11 +347,11 @@ trait Conversions {
.filter(p => p.name.forall(s => !s.equalsIgnoreCase("meta")))
.map(p =>
OUpload
.File(p.filename, p.headers.get(`Content-Type`).map(fromContentType), p.body)
.File(p.filename, p.headers.get[`Content-Type`].map(fromContentType), p.body)
)
for {
metaData <- meta
_ <- Effect[F].delay(logger.debug(s"Parsed upload meta data: $metaData"))
_ <- Async[F].delay(logger.debug(s"Parsed upload meta data: $metaData"))
tracker <- Ident.randomId[F]
} yield UploadData(metaData._1, metaData._2, files, prio, Some(tracker))
}

View File

@ -13,6 +13,7 @@ import org.http4s.circe.CirceEntityEncoder._
import org.http4s.dsl.Http4sDsl
import org.http4s.headers.ETag.EntityTag
import org.http4s.headers._
import org.typelevel.ci.CIString
object BinaryUtil {
@ -21,12 +22,15 @@ object BinaryUtil {
): F[Response[F]] = {
import dsl._
val mt = MediaType.unsafeParse(data.meta.mimetype.asString)
val ctype = `Content-Type`(mt)
val cntLen: Header = `Content-Length`.unsafeFromLong(data.meta.length)
val eTag: Header = ETag(data.meta.checksum)
val disp: Header =
`Content-Disposition`("inline", Map("filename" -> data.name.getOrElse("")))
val mt = MediaType.unsafeParse(data.meta.mimetype.asString)
val ctype = `Content-Type`(mt)
val cntLen = `Content-Length`.unsafeFromLong(data.meta.length)
val eTag = ETag(data.meta.checksum)
val disp =
`Content-Disposition`(
"inline",
Map(CIString("filename") -> data.name.getOrElse(""))
)
resp.map(r =>
if (r.status == NotModified) r.withHeaders(ctype, eTag, disp)
@ -52,13 +56,9 @@ object BinaryUtil {
false
}
def noPreview[F[_]: Sync: ContextShift](
blocker: Blocker,
req: Option[Request[F]]
): OptionT[F, Response[F]] =
def noPreview[F[_]: Async](req: Option[Request[F]]): OptionT[F, Response[F]] =
StaticFile.fromResource(
name = "/docspell/restserver/no-preview.svg",
blocker = blocker,
req = req,
preferGzipped = true,
classloader = getClass.getClassLoader().some

View File

@ -8,7 +8,7 @@ import docspell.restserver.Config
import org.http4s._
import org.http4s.headers._
import org.http4s.util.CaseInsensitiveString
import org.typelevel.ci.CIString
/** Obtain information about the client by inspecting the request.
*/
@ -35,23 +35,23 @@ object ClientRequestInfo {
xForwardedProto(req).orElse(clientConnectionProto(req))
private def host[F[_]](req: Request[F]): Option[String] =
req.headers.get(Host).map(_.host)
req.headers.get[Host].map(_.host)
private def xForwardedFor[F[_]](req: Request[F]): Option[String] =
req.headers
.get(`X-Forwarded-For`)
.get[`X-Forwarded-For`]
.flatMap(_.values.head)
.flatMap(inet => Option(inet.getHostName).orElse(Option(inet.getHostAddress)))
.map(ip => ip.fold(_.toUriString, _.toUriString))
private def xForwardedHost[F[_]](req: Request[F]): Option[String] =
req.headers
.get(CaseInsensitiveString("X-Forwarded-Host"))
.map(_.value)
.get(CIString("X-Forwarded-Host"))
.map(_.head.value)
private def xForwardedProto[F[_]](req: Request[F]): Option[String] =
req.headers
.get(CaseInsensitiveString("X-Forwarded-Proto"))
.map(_.value)
.get(CIString("X-Forwarded-Proto"))
.map(_.head.value)
private def clientConnectionProto[F[_]](req: Request[F]): Option[String] =
req.isSecure.map {
@ -61,8 +61,8 @@ object ClientRequestInfo {
private def xForwardedPort[F[_]](req: Request[F]): Option[Int] =
req.headers
.get(CaseInsensitiveString("X-Forwarded-Port"))
.map(_.value)
.get(CIString("X-Forwarded-Port"))
.map(_.head.value)
.flatMap(str => Either.catchNonFatal(str.toInt).toOption)
}

View File

@ -10,7 +10,7 @@ import org.http4s._
import org.http4s.headers._
object NoCacheMiddleware {
private val noCacheHeader: Header =
private val noCacheHeader =
`Cache-Control`(
NonEmptyList.of(
CacheDirective.`max-age`(Duration.zero.toScala),

View File

@ -10,7 +10,7 @@ trait ResponseGenerator[F[_]] {
self: Http4sDsl[F] =>
implicit final class EitherResponses[A, B](e: Either[A, B]) {
def toResponse(headers: Header*)(implicit
def toResponse(headers: Header.ToRaw*)(implicit
F: Applicative[F],
w0: EntityEncoder[F, A],
w1: EntityEncoder[F, B]
@ -23,7 +23,7 @@ trait ResponseGenerator[F[_]] {
implicit final class OptionResponse[A](o: Option[A]) {
def toResponse(
headers: Header*
headers: Header.ToRaw*
)(implicit F: Applicative[F], w0: EntityEncoder[F, A]): F[Response[F]] =
o.map(a => Ok(a)).getOrElse(NotFound()).map(_.withHeaders(headers: _*))
}

View File

@ -26,10 +26,10 @@ object Responses {
)
def forbidden[F[_]]: Response[F] =
pureForbidden.copy(body = pureForbidden.body.covary[F])
pureForbidden.covary[F].copy(body = pureForbidden.body.covary[F])
def unauthorized[F[_]]: Response[F] =
pureUnauthorized.copy(body = pureUnauthorized.body.covary[F])
pureUnauthorized.covary[F].copy(body = pureUnauthorized.body.covary[F])
def noCache[F[_]](r: Response[F]): Response[F] =
r.withHeaders(

View File

@ -11,12 +11,12 @@ import org.http4s._
import org.http4s.circe.CirceEntityEncoder._
import org.http4s.dsl.Http4sDsl
import org.http4s.server._
import org.http4s.util.CaseInsensitiveString
import org.typelevel.ci.CIString
object AdminRoutes {
private val adminHeader = CaseInsensitiveString("Docspell-Admin-Secret")
private val adminHeader = CIString("Docspell-Admin-Secret")
def apply[F[_]: Effect](cfg: Config.AdminEndpoint)(
def apply[F[_]: Async](cfg: Config.AdminEndpoint)(
f: HttpRoutes[F]
): HttpRoutes[F] = {
val dsl: Http4sDsl[F] = new Http4sDsl[F] {}
@ -34,7 +34,7 @@ object AdminRoutes {
else middleware(AuthedRoutes(authReq => f.run(authReq.req)))
}
private def checkSecret[F[_]: Effect](
private def checkSecret[F[_]: Async](
cfg: Config.AdminEndpoint
): Kleisli[F, Request[F], Either[String, Unit]] =
Kleisli(req =>
@ -46,7 +46,7 @@ object AdminRoutes {
)
private def extractSecret[F[_]](req: Request[F]): Option[String] =
req.headers.get(adminHeader).map(_.value)
req.headers.get(adminHeader).map(_.head.value)
private def compareSecret(s1: String)(s2: String): Boolean =
s1.length > 0 && s1.length == s2.length &&

View File

@ -1,6 +1,6 @@
package docspell.restserver.routes
import cats.effect.Effect
import cats.effect.Async
import cats.implicits._
import docspell.backend.BackendApp
@ -15,7 +15,7 @@ import org.http4s.dsl.Http4sDsl
object AttachmentMultiRoutes extends MultiIdSupport {
def apply[F[_]: Effect](
def apply[F[_]: Async](
backend: BackendApp[F],
user: AuthToken
): HttpRoutes[F] = {

View File

@ -22,8 +22,7 @@ import org.http4s.headers._
object AttachmentRoutes {
def apply[F[_]: Effect: ContextShift](
blocker: Blocker,
def apply[F[_]: Async](
backend: BackendApp[F],
user: AuthToken
): HttpRoutes[F] = {
@ -51,7 +50,7 @@ object AttachmentRoutes {
case req @ GET -> Root / Ident(id) =>
for {
fileData <- backend.itemSearch.findAttachment(id, user.account.collective)
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
inm = req.headers.get[`If-None-Match`].flatMap(_.tags)
matches = BinaryUtil.matchETag(fileData.map(_.meta), inm)
resp <-
fileData
@ -74,7 +73,7 @@ object AttachmentRoutes {
case req @ GET -> Root / Ident(id) / "original" =>
for {
fileData <- backend.itemSearch.findAttachmentSource(id, user.account.collective)
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
inm = req.headers.get[`If-None-Match`].flatMap(_.tags)
matches = BinaryUtil.matchETag(fileData.map(_.meta), inm)
resp <-
fileData
@ -99,7 +98,7 @@ object AttachmentRoutes {
for {
fileData <-
backend.itemSearch.findAttachmentArchive(id, user.account.collective)
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
inm = req.headers.get[`If-None-Match`].flatMap(_.tags)
matches = BinaryUtil.matchETag(fileData.map(_.meta), inm)
resp <-
fileData
@ -116,7 +115,7 @@ object AttachmentRoutes {
for {
fileData <-
backend.itemSearch.findAttachmentPreview(id, user.account.collective)
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
inm = req.headers.get[`If-None-Match`].flatMap(_.tags)
matches = BinaryUtil.matchETag(fileData.map(_.meta), inm)
fallback = flag.getOrElse(false)
resp <-
@ -126,7 +125,7 @@ object AttachmentRoutes {
else makeByteResp(data)
}
.getOrElse(
if (fallback) BinaryUtil.noPreview(blocker, req.some).getOrElseF(notFound)
if (fallback) BinaryUtil.noPreview(req.some).getOrElseF(notFound)
else notFound
)
} yield resp
@ -158,7 +157,7 @@ object AttachmentRoutes {
// it redirects currently to viewerjs
val attachUrl = s"/api/v1/sec/attachment/${id.id}"
val path = s"/app/assets${Webjars.viewerjs}/ViewerJS/index.html#$attachUrl"
SeeOther(Location(Uri(path = path)))
SeeOther(Location(Uri(path = Uri.Path.unsafeFromString(path))))
case GET -> Root / Ident(id) / "meta" =>
for {

View File

@ -14,7 +14,7 @@ import org.http4s.server._
object Authenticate {
def authenticateRequest[F[_]: Effect](
def authenticateRequest[F[_]: Async](
auth: (String, Option[String]) => F[Login.Result]
)(req: Request[F]): F[Login.Result] =
CookieData.authenticator(req) match {
@ -30,7 +30,7 @@ object Authenticate {
}
}
def of[F[_]: Effect](S: Login[F], cfg: Login.Config)(
def of[F[_]: Async](S: Login[F], cfg: Login.Config)(
pf: PartialFunction[AuthedRequest[F, AuthToken], F[Response[F]]]
): HttpRoutes[F] = {
val dsl: Http4sDsl[F] = new Http4sDsl[F] {}
@ -47,7 +47,7 @@ object Authenticate {
middleware(AuthedRoutes.of(pf))
}
def apply[F[_]: Effect](S: Login[F], cfg: Login.Config)(
def apply[F[_]: Async](S: Login[F], cfg: Login.Config)(
f: AuthToken => HttpRoutes[F]
): HttpRoutes[F] = {
val dsl: Http4sDsl[F] = new Http4sDsl[F] {}
@ -64,7 +64,7 @@ object Authenticate {
middleware(AuthedRoutes(authReq => f(authReq.context).run(authReq.req)))
}
private def getUser[F[_]: Effect](
private def getUser[F[_]: Async](
auth: (String, Option[String]) => F[Login.Result]
): Kleisli[F, Request[F], Either[String, AuthToken]] =
Kleisli(r => authenticateRequest(auth)(r).map(_.toEither))

View File

@ -14,7 +14,7 @@ import org.http4s.dsl.Http4sDsl
object CalEventCheckRoutes {
def apply[F[_]: Effect](): HttpRoutes[F] = {
def apply[F[_]: Async](): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._

View File

@ -16,7 +16,7 @@ import org.http4s.dsl.Http4sDsl
object CheckFileRoutes {
def secured[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def secured[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
import dsl._
@ -30,7 +30,7 @@ object CheckFileRoutes {
}
}
def open[F[_]: Effect](backend: BackendApp[F]): HttpRoutes[F] = {
def open[F[_]: Async](backend: BackendApp[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
import dsl._

View File

@ -16,7 +16,7 @@ import org.http4s.dsl.Http4sDsl
object ClientSettingsRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._

View File

@ -19,7 +19,7 @@ import org.http4s.dsl.Http4sDsl
object CollectiveRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
import dsl._

View File

@ -23,7 +23,7 @@ import org.http4s.dsl.Http4sDsl
object CustomFieldRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
import dsl._

View File

@ -18,7 +18,7 @@ import org.http4s.dsl.Http4sDsl
object EquipmentRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._

View File

@ -20,7 +20,7 @@ import org.http4s.dsl.Http4sDsl
object FolderRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
import dsl._

View File

@ -15,7 +15,7 @@ import org.http4s.dsl.Http4sDsl
object FullTextIndexRoutes {
def secured[F[_]: Effect](
def secured[F[_]: Async](
cfg: Config,
backend: BackendApp[F],
user: AuthToken
@ -33,7 +33,7 @@ object FullTextIndexRoutes {
}
}
def admin[F[_]: Effect](cfg: Config, backend: BackendApp[F]): HttpRoutes[F] =
def admin[F[_]: Async](cfg: Config, backend: BackendApp[F]): HttpRoutes[F] =
if (!cfg.fullTextSearch.enabled) notFound[F]
else {
val dsl = Http4sDsl[F]
@ -47,6 +47,6 @@ object FullTextIndexRoutes {
}
}
private def notFound[F[_]: Effect]: HttpRoutes[F] =
private def notFound[F[_]: Async]: HttpRoutes[F] =
Responses.notFoundRoute[F]
}

View File

@ -16,13 +16,13 @@ import org.http4s.circe.CirceEntityEncoder._
import org.http4s.dsl.Http4sDsl
import org.http4s.headers.{Authorization, `WWW-Authenticate`}
import org.http4s.multipart.Multipart
import org.http4s.util.CaseInsensitiveString
import org.log4s.getLogger
import org.typelevel.ci.CIString
object IntegrationEndpointRoutes {
private[this] val logger = getLogger
def open[F[_]: Effect](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
def open[F[_]: Async](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
@ -61,12 +61,12 @@ object IntegrationEndpointRoutes {
}
}
def checkEnabled[F[_]: Effect](
def checkEnabled[F[_]: Async](
cfg: Config.IntegrationEndpoint
): EitherT[F, Response[F], Unit] =
EitherT.cond[F](cfg.enabled, (), Response.notFound[F])
def authRequest[F[_]: Effect](
def authRequest[F[_]: Async](
req: Request[F],
cfg: Config.IntegrationEndpoint
): EitherT[F, Response[F], Unit] = {
@ -77,7 +77,7 @@ object IntegrationEndpointRoutes {
service.run(req).toLeft(())
}
def lookupCollective[F[_]: Effect](
def lookupCollective[F[_]: Async](
coll: Ident,
backend: BackendApp[F]
): EitherT[F, Response[F], Unit] =
@ -86,7 +86,7 @@ object IntegrationEndpointRoutes {
res <- EitherT.cond[F](opt.exists(_.integrationEnabled), (), Response.notFound[F])
} yield res
def uploadFile[F[_]: Effect](
def uploadFile[F[_]: Async](
coll: Ident,
backend: BackendApp[F],
cfg: Config,
@ -111,48 +111,48 @@ object IntegrationEndpointRoutes {
}
object HeaderAuth {
def apply[F[_]: Effect](cfg: Config.IntegrationEndpoint.HttpHeader): HttpRoutes[F] =
def apply[F[_]: Async](cfg: Config.IntegrationEndpoint.HttpHeader): HttpRoutes[F] =
if (cfg.enabled) checkHeader(cfg)
else HttpRoutes.empty[F]
def checkHeader[F[_]: Effect](
def checkHeader[F[_]: Async](
cfg: Config.IntegrationEndpoint.HttpHeader
): HttpRoutes[F] =
HttpRoutes { req =>
val h = req.headers.find(_.name == CaseInsensitiveString(cfg.headerName))
if (h.exists(_.value == cfg.headerValue)) OptionT.none[F, Response[F]]
val h = req.headers.get(CIString(cfg.headerName))
if (h.exists(_.head.value == cfg.headerValue)) OptionT.none[F, Response[F]]
else OptionT.pure(Responses.forbidden[F])
}
}
object SourceIpAuth {
def apply[F[_]: Effect](cfg: Config.IntegrationEndpoint.AllowedIps): HttpRoutes[F] =
def apply[F[_]: Async](cfg: Config.IntegrationEndpoint.AllowedIps): HttpRoutes[F] =
if (cfg.enabled) checkIps(cfg)
else HttpRoutes.empty[F]
def checkIps[F[_]: Effect](
def checkIps[F[_]: Async](
cfg: Config.IntegrationEndpoint.AllowedIps
): HttpRoutes[F] =
HttpRoutes { req =>
//The `req.from' take the X-Forwarded-For header into account,
//which is not desirable here. The `http-header' auth config
//can be used to authenticate based on headers.
val from = req.remote.flatMap(remote => Option(remote.getAddress))
val from = req.remote.map(_.host)
if (from.exists(cfg.containsAddress)) OptionT.none[F, Response[F]]
else OptionT.pure(Responses.forbidden[F])
}
}
object HttpBasicAuth {
def apply[F[_]: Effect](cfg: Config.IntegrationEndpoint.HttpBasic): HttpRoutes[F] =
def apply[F[_]: Async](cfg: Config.IntegrationEndpoint.HttpBasic): HttpRoutes[F] =
if (cfg.enabled) checkHttpBasic(cfg)
else HttpRoutes.empty[F]
def checkHttpBasic[F[_]: Effect](
def checkHttpBasic[F[_]: Async](
cfg: Config.IntegrationEndpoint.HttpBasic
): HttpRoutes[F] =
HttpRoutes { req =>
req.headers.get(Authorization) match {
req.headers.get[Authorization] match {
case Some(auth) =>
auth.credentials match {
case BasicCredentials(user, pass)

View File

@ -17,7 +17,7 @@ import org.http4s.dsl.Http4sDsl
object ItemMultiRoutes extends MultiIdSupport {
def apply[F[_]: Effect](
def apply[F[_]: Async](
backend: BackendApp[F],
user: AuthToken
): HttpRoutes[F] = {

View File

@ -32,9 +32,8 @@ import org.log4s._
object ItemRoutes {
private[this] val logger = getLogger
def apply[F[_]: Effect: ContextShift](
def apply[F[_]: Async](
cfg: Config,
blocker: Blocker,
backend: BackendApp[F],
user: AuthToken
): HttpRoutes[F] = {
@ -331,7 +330,7 @@ object ItemRoutes {
NotFound(BasicResult(false, "Not found"))
for {
preview <- backend.itemSearch.findItemPreview(id, user.account.collective)
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
inm = req.headers.get[`If-None-Match`].flatMap(_.tags)
matches = BinaryUtil.matchETag(preview.map(_.meta), inm)
fallback = flag.getOrElse(false)
resp <-
@ -341,7 +340,7 @@ object ItemRoutes {
else BinaryUtil.makeByteResp(dsl)(data).map(Responses.noCache)
}
.getOrElse(
if (fallback) BinaryUtil.noPreview(blocker, req.some).getOrElseF(notFound)
if (fallback) BinaryUtil.noPreview(req.some).getOrElseF(notFound)
else notFound
)
} yield resp

View File

@ -16,7 +16,7 @@ import org.http4s.dsl.Http4sDsl
object JobQueueRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._

View File

@ -17,7 +17,7 @@ import org.http4s.dsl.Http4sDsl
object LoginRoutes {
def login[F[_]: Effect](S: Login[F], cfg: Config): HttpRoutes[F] = {
def login[F[_]: Async](S: Login[F], cfg: Config): HttpRoutes[F] = {
val dsl: Http4sDsl[F] = new Http4sDsl[F] {}
import dsl._
@ -32,7 +32,7 @@ object LoginRoutes {
}
}
def session[F[_]: Effect](S: Login[F], cfg: Config, token: AuthToken): HttpRoutes[F] = {
def session[F[_]: Async](S: Login[F], cfg: Config, token: AuthToken): HttpRoutes[F] = {
val dsl: Http4sDsl[F] = new Http4sDsl[F] {}
import dsl._
@ -56,7 +56,7 @@ object LoginRoutes {
private def getBaseUrl[F[_]](cfg: Config, req: Request[F]): LenientUri =
ClientRequestInfo.getBaseUrl(cfg, req)
private def makeResponse[F[_]: Effect](
private def makeResponse[F[_]: Async](
dsl: Http4sDsl[F],
cfg: Config,
req: Request[F],

View File

@ -19,7 +19,7 @@ import org.http4s.dsl.Http4sDsl
object MailSendRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._

View File

@ -22,7 +22,7 @@ import org.http4s.dsl.Http4sDsl
object MailSettingsRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._

View File

@ -20,7 +20,7 @@ import org.http4s.dsl.Http4sDsl
object NotifyDueItemsRoutes {
def apply[F[_]: Effect](
def apply[F[_]: Async](
cfg: Config,
backend: BackendApp[F],
user: AuthToken

View File

@ -18,7 +18,7 @@ import org.http4s.dsl.Http4sDsl
object OrganizationRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._

View File

@ -21,7 +21,7 @@ import org.log4s._
object PersonRoutes {
private[this] val logger = getLogger
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._

View File

@ -19,7 +19,7 @@ import org.log4s._
object RegisterRoutes {
private[this] val logger = getLogger
def apply[F[_]: Effect](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
import dsl._

View File

@ -18,7 +18,7 @@ import org.http4s.dsl.Http4sDsl
object ScanMailboxRoutes {
def apply[F[_]: Effect](
def apply[F[_]: Async](
backend: BackendApp[F],
user: AuthToken
): HttpRoutes[F] = {

View File

@ -17,7 +17,7 @@ import org.http4s.dsl.Http4sDsl
object SentMailRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._

View File

@ -17,7 +17,7 @@ import org.http4s.dsl.Http4sDsl
object SourceRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
import dsl._

View File

@ -17,7 +17,7 @@ import org.http4s.dsl.Http4sDsl
object TagRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
import dsl._

View File

@ -20,7 +20,7 @@ import org.log4s._
object UploadRoutes {
private[this] val logger = getLogger
def secured[F[_]: Effect](
def secured[F[_]: Async](
backend: BackendApp[F],
cfg: Config,
user: AuthToken
@ -39,7 +39,7 @@ object UploadRoutes {
}
}
def open[F[_]: Effect](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
def open[F[_]: Async](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
import dsl._
@ -62,7 +62,7 @@ object UploadRoutes {
}
}
private def submitFiles[F[_]: Effect](
private def submitFiles[F[_]: Async](
backend: BackendApp[F],
cfg: Config,
accOrSrc: Either[Ident, AccountId]

View File

@ -17,7 +17,7 @@ import org.http4s.dsl.Http4sDsl
object UserRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
@ -63,7 +63,7 @@ object UserRoutes {
}
}
def admin[F[_]: Effect](backend: BackendApp[F]): HttpRoutes[F] = {
def admin[F[_]: Async](backend: BackendApp[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._

View File

@ -30,14 +30,12 @@ object TemplateRoutes {
def serviceWorker: HttpRoutes[F]
}
def apply[F[_]: Effect](blocker: Blocker, cfg: Config)(implicit
C: ContextShift[F]
): InnerRoutes[F] = {
def apply[F[_]: Async](cfg: Config): InnerRoutes[F] = {
val indexTemplate = memo(
loadResource("/index.html").flatMap(loadTemplate(_, blocker))
loadResource("/index.html").flatMap(loadTemplate(_))
)
val docTemplate = memo(loadResource("/doc.html").flatMap(loadTemplate(_, blocker)))
val swTemplate = memo(loadResource("/sw.js").flatMap(loadTemplate(_, blocker)))
val docTemplate = memo(loadResource("/doc.html").flatMap(loadTemplate(_)))
val swTemplate = memo(loadResource("/sw.js").flatMap(loadTemplate(_)))
val dsl = new Http4sDsl[F] {}
import dsl._
@ -84,12 +82,10 @@ object TemplateRoutes {
r.pure[F]
}
def loadUrl[F[_]: Sync](url: URL, blocker: Blocker)(implicit
C: ContextShift[F]
): F[String] =
def loadUrl[F[_]: Sync](url: URL): F[String] =
Stream
.bracket(Sync[F].delay(url.openStream))(in => Sync[F].delay(in.close()))
.flatMap(in => fs2.io.readInputStream(in.pure[F], 64 * 1024, blocker, false))
.flatMap(in => fs2.io.readInputStream(in.pure[F], 64 * 1024, false))
.through(text.utf8Decode)
.compile
.fold("")(_ + _)
@ -102,10 +98,8 @@ object TemplateRoutes {
}
}
def loadTemplate[F[_]: Sync](url: URL, blocker: Blocker)(implicit
C: ContextShift[F]
): F[Template] =
loadUrl[F](url, blocker).flatMap(s => parseTemplate(s)).map { t =>
def loadTemplate[F[_]: Sync](url: URL): F[Template] =
loadUrl[F](url).flatMap(s => parseTemplate(s)).map { t =>
logger.info(s"Compiled template $url")
t
}

View File

@ -27,19 +27,18 @@ object WebjarRoutes {
".xml"
)
def appRoutes[F[_]: Effect](
blocker: Blocker
)(implicit CS: ContextShift[F]): HttpRoutes[F] =
def appRoutes[F[_]: Async]: HttpRoutes[F] =
Kleisli {
case req if req.method == Method.GET =>
val p = req.pathInfo
if (p.contains("..") || !suffixes.exists(p.endsWith(_)))
val p = req.pathInfo.renderString
val last = req.pathInfo.segments.lastOption.map(_.encoded).getOrElse("")
val containsColon = req.pathInfo.segments.exists(_.encoded.contains(".."))
if (containsColon || !suffixes.exists(last.endsWith(_)))
OptionT.pure(Response.notFound[F])
else
StaticFile
.fromResource(
s"/META-INF/resources/webjars$p",
blocker,
Some(req),
EnvMode.current.isProd
)