package bad.robot.temperature.server import java.lang.Math._ import java.time.Clock import java.util.concurrent.Executors._ import java.util.concurrent.ExecutorService import bad.robot.temperature.JsonToCsv import bad.robot.temperature.ds18b20.{SensorFile, SensorReader} import bad.robot.temperature.rrd.Host import bad.robot.temperature.task.TemperatureMachineThreadFactory import fs2.Stream import fs2.Scheduler import fs2.StreamApp._ import cats.implicits._ import scala.concurrent.ExecutionContext import cats.effect.IO import org.http4s.HttpService import org.http4s.server.blaze.BlazeBuilder import org.http4s.server.middleware.{CORS, GZip} object HttpServer { def apply(port: Int, monitored: List[Host]): HttpServer = { new HttpServer(port, monitored) } } class HttpServer(port: Int, monitored: List[Host]) { private val DefaultHttpExecutorService: ExecutorService = { newFixedThreadPool(max(4, Runtime.getRuntime.availableProcessors), TemperatureMachineThreadFactory("http-server")) } def asStream(temperatures: AllTemperatures, connections: Connections): Stream[IO, ExitCode] = { import scala.concurrent.ExecutionContext.Implicits.global // todo replace with explicit one for { scheduler <- Scheduler[IO](corePoolSize = 1) exitCode <- build(temperatures, connections, scheduler).serve } yield exitCode } private[server] def build(temperatures: AllTemperatures, connections: Connections, scheduler: Scheduler): BlazeBuilder[IO] = { BlazeBuilder[IO] .withWebSockets(true) .withExecutionContext(ExecutionContext.fromExecutorService(DefaultHttpExecutorService)) .bindHttp(port, "0.0.0.0") .mountService(services(scheduler, temperatures, connections), "/") } private def services(scheduler: Scheduler, temperatures: AllTemperatures, connections: Connections): HttpService[IO] = { GZip( CORS( TemperatureEndpoint(scheduler, SensorReader(Host.local, SensorFile.find()), temperatures, connections) <+> ConnectionsEndpoint(connections)(Clock.systemDefaultZone) <+> LogEndpoint() <+> ExportEndpoint(JsonFile.load, JsonToCsv.DefaultTimeFormatter) <+> VersionEndpoint() <+> StaticFiles() <+> StaticResources() ) ) } }