All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.cheleb.ziolaminartapir.Session.scala Maven / Gradle / Ivy

package dev.cheleb.ziolaminartapir

import scala.scalajs.js.Date

import com.raquo.airstream.core.Signal
import com.raquo.laminar.api.L.*

import zio.json.*
import dev.cheleb.ziojwt.WithToken
import sttp.model.Uri

trait Session[UserToken <: WithToken] {

  /** This method will return a Signal that will be updated when the user state
    * changes.
    *
    * It takes two arguments to produce a value depending on the user state
    *
    * @param withoutSession
    *   the value to produce when the user is not logged in
    * @param withSession
    *   the value to produce when the user is logged in
    * @return
    */
  def apply[A](withoutSession: => A)(
      withSession: UserToken => A
  ): Signal[A]
  def whenActive[A](callback: => A): Signal[Option[A]]

  /** This method returns true user is active.
    *
    * @return
    */
  def isActive: Boolean

  /** Save the token in the storage. Tokens are stored by issuer (the host and
    * port of the issuer).
    *
    * @param issuer
    * @param token
    */
  def saveToken(issuer: Uri, token: UserToken): Unit

  /** Get the token from the storage. Tokens are stored by issuer (the host and
    * port of the issuer).
    *
    * @param issuer
    * @return
    */
  def getToken(issuer: Uri): Option[UserToken]

  /** Load the user state from the storage with Same Origin issuer. This method
    * is used to log in the user.
    */
  def loadUserState(): Unit

  /** Load the user state from the storage. This method is used to log in the
    *
    * @param issuer
    */
  def loadUserState(issuer: Uri): Unit

  /** Clear the user state. This method is used to log out the user. It should
    * remove the token from the session and the storage.
    */
  def clearUserState(): Unit
}

class SessionLive[UserToken <: WithToken](using JsonCodec[UserToken])
    extends Session[UserToken] {
  val userState: Var[Option[UserToken]] = Var(Option.empty[UserToken])

  /** This method is used to produce a key to store the token in the storage.
    *
    * @param issuer
    * @return
    */
  private def userTokenKey(issuer: Uri): String =
    (for {
      host <- issuer.host
      port <- issuer.port
    } yield s"userToken@$host:$port")
      .getOrElse(s"userToken@${issuer.toString}")

  def apply[A](
      withoutSession: => A
  )(withSession: UserToken => A): Signal[A] =
    userState.signal.map {
      case Some(userToken) => withSession(userToken)
      case None            => withoutSession
    }

  def whenActive[A](callback: => A): Signal[Option[A]] =
    userState.signal.map(_.map(_ => callback))

  def isActive = userState
    .now()
    .map(_.expiration * 1000 > new Date().getTime()) match {
    case Some(true) => true
    case Some(false) =>
      userState.set(Option.empty[UserToken])
      Storage.removeAll()
      false
    case None => false
  }

  def saveToken(issuer: Uri, token: UserToken): Unit = {
    userState.set(Option(token))
    Storage.set(userTokenKey(issuer), token)
  }

  def getToken(issuer: Uri): Option[UserToken] =
    loadUserState(issuer)
    userState.now()

  def loadUserState(): Unit =
    loadUserState(SameOriginBackendClientLive.backendBaseURL)
  def loadUserState(issuer: Uri): Unit =
    Storage
      .get[UserToken](userTokenKey(issuer))
      .foreach {
        case exp: WithToken if exp.expiration * 1000 < new Date().getTime() =>
          Storage.remove(userTokenKey(issuer))
        case token =>
          userState.now() match
            case Some(value) if token != value => userState.set(Some(token))
            case None                          => userState.set(Some(token))
            case _                             => ()
      }

  def clearUserState(): Unit =
    userState.set(Option.empty[UserToken])
    Storage.removeAll()
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy