|
|
|
@ -4,11 +4,13 @@ import io.getquill.context.qzio.{ZioContext, ZioJdbcContext}
|
|
|
|
|
import io.getquill.context.sql.idiom.SqlIdiom
|
|
|
|
|
import io.getquill.idiom.Idiom
|
|
|
|
|
import io.getquill.{Literal, NamingStrategy, SqliteDialect}
|
|
|
|
|
import org.apache.logging.log4j.LogManager
|
|
|
|
|
import org.apache.logging.log4j.{LogManager, Logger}
|
|
|
|
|
import org.tbasket.auth.Authenticator
|
|
|
|
|
import org.tbasket.data.DatabaseContext
|
|
|
|
|
import org.tbasket.endpoint.Endpoint.LOG
|
|
|
|
|
import org.tbasket.handler.LoginPageHandler
|
|
|
|
|
import org.tbasket.endpoint.Endpoint.{Log, app}
|
|
|
|
|
import org.tbasket.error.*
|
|
|
|
|
import org.tbasket.handler.HandlerUtils.errorBody
|
|
|
|
|
import org.tbasket.handler.{HandlerUtils, LoginPageHandler}
|
|
|
|
|
import zio.*
|
|
|
|
|
import zio.http.*
|
|
|
|
|
import zio.http.ServerConfig.LeakDetectionLevel
|
|
|
|
@ -17,17 +19,35 @@ import zio.http.model.Status
|
|
|
|
|
import zio.http.model.Status.InternalServerError
|
|
|
|
|
import zio.http.netty.client.ConnectionPool
|
|
|
|
|
|
|
|
|
|
import java.sql.Timestamp
|
|
|
|
|
import javax.sql.DataSource
|
|
|
|
|
import scala.collection.mutable
|
|
|
|
|
|
|
|
|
|
class Endpoint(port: Int):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// set generic required headers
|
|
|
|
|
private def applyGenerics(response: Response): Response =
|
|
|
|
|
response.withAccessControlAllowOrigin("*")
|
|
|
|
|
val run =
|
|
|
|
|
val config = ServerConfig.default
|
|
|
|
|
.port(port)
|
|
|
|
|
.leakDetection(LeakDetectionLevel.PARANOID)
|
|
|
|
|
|
|
|
|
|
val serverConfigLayer = ServerConfig.live(config)
|
|
|
|
|
Server.install(app).flatMap { port =>
|
|
|
|
|
Log.info(s"Listening API entries on $port")
|
|
|
|
|
ZIO.never
|
|
|
|
|
}.provideSome[DatabaseContext & Authenticator & DataSource](
|
|
|
|
|
Scope.default,
|
|
|
|
|
serverConfigLayer,
|
|
|
|
|
ConnectionPool.fixed(4),
|
|
|
|
|
ClientConfig.default,
|
|
|
|
|
Server.live,
|
|
|
|
|
Client.live
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
private val app = Http.collectZIO[Request] {
|
|
|
|
|
object Endpoint:
|
|
|
|
|
final val Log = LogManager.getLogger("API")
|
|
|
|
|
|
|
|
|
|
private def tryHandle(r: Request) = r match
|
|
|
|
|
case r@POST -> _ / "login" =>
|
|
|
|
|
LoginPageHandler.post(r)
|
|
|
|
|
|
|
|
|
@ -35,56 +55,69 @@ class Endpoint(port: Int):
|
|
|
|
|
val ipInsights = r.remoteAddress
|
|
|
|
|
.map(ip => s": request received from $ip.")
|
|
|
|
|
.getOrElse("")
|
|
|
|
|
LOG.error(
|
|
|
|
|
Log.error(
|
|
|
|
|
s"Was unable to find a handler for request '$path' with method $method ${ipInsights}"
|
|
|
|
|
)
|
|
|
|
|
ZIO.succeed(Response(Status.NotFound))
|
|
|
|
|
}.catchAllCause(handleUnexpectedError)
|
|
|
|
|
|
|
|
|
|
def handle(r: Request) = tryHandle(r)
|
|
|
|
|
.catchSome(respondToRegulars)
|
|
|
|
|
.catchAllCause(handleUnexpectedError)
|
|
|
|
|
.map(applyGenerics)
|
|
|
|
|
|
|
|
|
|
private def handleUnexpectedError(cause: Cause[Throwable]): HttpApp[Any, Throwable] = {
|
|
|
|
|
val app = Http.collectZIO[Request] { r =>
|
|
|
|
|
handle(r)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set generic required headers
|
|
|
|
|
private def applyGenerics(response: Response): Response =
|
|
|
|
|
response.withAccessControlAllowOrigin("*")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private def respondToRegulars: PartialFunction[Object, Task[Response]] = {
|
|
|
|
|
case InvalidRequest(msg, cause) => ZIO.attempt(Response(
|
|
|
|
|
status = Status.BadRequest,
|
|
|
|
|
body = errorBody("invalid request", s"$cause: $msg")
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
case InternalError(e) =>
|
|
|
|
|
Log.error("Internal error : ")
|
|
|
|
|
Log.throwing(e)
|
|
|
|
|
ZIO.attempt(Response(
|
|
|
|
|
status = Status.InternalServerError,
|
|
|
|
|
body = errorBody("invalid request", s"internal error: please contact support, timestamp : $timestamp")
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def timestamp = new Timestamp(java.lang.System.currentTimeMillis())
|
|
|
|
|
|
|
|
|
|
private def handleUnexpectedError(cause: Object): Task[Response] = {
|
|
|
|
|
def report(kind: String, value: Throwable = null, trace: StackTrace = StackTrace.none) =
|
|
|
|
|
LOG.error(s"Received unhandled $kind cause ${if value == null then "" else ": " + value}")
|
|
|
|
|
LOG.error(trace)
|
|
|
|
|
Log.error(s"Received unhandled $kind cause ${if value == null then "" else ": " + value}")
|
|
|
|
|
Log.error(trace)
|
|
|
|
|
|
|
|
|
|
cause match
|
|
|
|
|
case e: Throwable => report(e.getMessage, e)
|
|
|
|
|
case Cause.Empty => report("empty")
|
|
|
|
|
case Cause.Fail(e, trace) => report("failure", e, trace)
|
|
|
|
|
case Cause.Fail(e: Throwable, trace) => report("failure", e, trace)
|
|
|
|
|
case Cause.Fail(_, trace) => report("failure", null, trace)
|
|
|
|
|
case Cause.Die(e, trace) => report("die", e, trace)
|
|
|
|
|
case Cause.Interrupt(fiberId, e) => report(s"interruption of $fiberId", null, e)
|
|
|
|
|
case Cause.Stackless(cause, _) =>
|
|
|
|
|
LOG.error("stackless error :")
|
|
|
|
|
Log.error("stackless error :")
|
|
|
|
|
handleUnexpectedError(cause)
|
|
|
|
|
case Cause.Then(left, right) =>
|
|
|
|
|
handleUnexpectedError(left)
|
|
|
|
|
LOG.error("**THEN this error occurred : **")
|
|
|
|
|
Log.error("**THEN this error occurred : **")
|
|
|
|
|
handleUnexpectedError(right)
|
|
|
|
|
case Cause.Both(left, right) =>
|
|
|
|
|
handleUnexpectedError(left)
|
|
|
|
|
LOG.error("**AND this error also occurred (async) : **")
|
|
|
|
|
Log.error("**AND this error also occurred (async) : **")
|
|
|
|
|
handleUnexpectedError(right)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Http.succeed(Response.status(InternalServerError))
|
|
|
|
|
ZIO.attempt(Response(
|
|
|
|
|
status = Status.InternalServerError,
|
|
|
|
|
body = errorBody("internal", s"internal error, please contact support (timestamp: $timestamp")
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val run =
|
|
|
|
|
val config = ServerConfig.default
|
|
|
|
|
.port(port)
|
|
|
|
|
.leakDetection(LeakDetectionLevel.PARANOID)
|
|
|
|
|
|
|
|
|
|
val serverConfigLayer = ServerConfig.live(config)
|
|
|
|
|
Server.install(app).flatMap { port =>
|
|
|
|
|
LOG.info(s"Listening API entries on $port")
|
|
|
|
|
ZIO.never
|
|
|
|
|
}.provideSome[DatabaseContext & Authenticator & DataSource](
|
|
|
|
|
Scope.default,
|
|
|
|
|
serverConfigLayer,
|
|
|
|
|
ConnectionPool.fixed(4),
|
|
|
|
|
ClientConfig.default,
|
|
|
|
|
Server.live,
|
|
|
|
|
Client.live
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
object Endpoint:
|
|
|
|
|
final val LOG = LogManager.getLogger("API")
|
|
|
|
|