Skip to content

Instantly share code, notes, and snippets.

View Algiras's full-sized avatar

Algimantas Krasauskas Algiras

  • Vilnius, Lithuania
View GitHub Profile
trait Topic[VReq, V] {
def record(request: VReq): RIO[Blocking, UUID]
def records: ZManaged[RecordConsumer.Env, Throwable, ZStream[Any, Nothing, V]]
}
// Job description
case class CronJob(id: UUID, cronString: CronExpr)
// App state
case class CronulaSate(jobs: Map[UUID, CronJob] = Map.empty[UUID, CronJob])
// Events
sealed trait CronulaEvent
case class RecordJob(id: UUID, cronString: CronExpr) extends CronulaEvent
case class DeleteJob(id: UUID) extends CronulaEvent
case class Updatejob(id: UUID, cronString: CronExpr) extends CronulaEvent
def run(args: List[String]): IO[ExitCode] = (for {
config <- loadConfigF[IO, Config]
userCredentialStore <- UserStore(config.users.head, config.users.tail: _*)
http <- httpApp(userCredentialStore)
res <- BlazeServerBuilder[IO]
.bindHttp(host = config.http.host, port = config.http.port.getOrElse(0))
.withHttpApp(http)
.serve
.compile
.drain
case class HttpConfig(host: String, port: Option[Int])
case class Config(http: HttpConfig, users: List[UsernamePasswordCredentials])
http {
host = ${ENV_PROVIDED_HOST}
port = ${ENV_PROVIDED_PORT}
}
users = [
{
username = ${ENV_PROVIDED_USERNAME}
password = ${ENV_PROVIDED_PASSWORD}
}
]
def authenticator(userStore: IdentityStore[IO, UserId, User]) = for {
tokens <- Ref.of[IO, Map[SecureRandomId, TSecBearerToken[UserId]]](Map.empty)
} yield BearerTokenAuthenticator[IO, UserId, User](
userStore,
tokenStore(tokens),
TSecTokenSettings(
expiryDuration = 10.minutes,
maxIdle = None
))
def httpApp(userStore: UserStore) = authenticator
implicit val loginUserDecoder: Decoder[UsernamePasswordCredentials] = deriveDecoder
implicit val entityLoginUserDecoder: EntityDecoder[IO, UsernamePasswordCredentials] =
jsonOf[IO, UsernamePasswordCredentials]
def loginRoute(
auth: BearerTokenAuthenticator[IO, UserId, User],
checkPassword: UsernamePasswordCredentials => IO[Option[User]]): HttpRoutes[IO] =
HttpRoutes.of[IO] {
case req @ POST -> Root / "login" =>
(for {
user <- req.as[UsernamePasswordCredentials]
object UserStore {
def apply(user: UsernamePasswordCredentials, users: UsernamePasswordCredentials*): IO[UserStore] =
for {
userList <- (user +: users)
.map(u => UserStore.newUser(u.username, u.password))
.toList
.sequence
users <- Ref.of[IO, Map[UserId, User]](userList.map(u => u.id -> u).toMap)
} yield
new UserStore(
def validateUser(credentials: UsernamePasswordCredentials)(
users: List[User]): IO[Option[User]] =
users.findM(
user =>
BCrypt
.checkpwBool[IO](credentials.password, user.password)
.map(_ && credentials.username == user.username),
)
def newUser(username: String, password: String): IO[User] =
Applicative[IO].map2(
FUUID.randomFUUID[IO].map(tagFUUIDAsUserId),
BCrypt.hashpw[IO](password)
)(User(_, username, _))