Caching and user data endpoint
This commit is contained in:
@@ -1,15 +1,26 @@
|
||||
package controllers
|
||||
|
||||
import javax.inject._
|
||||
import play.api._
|
||||
import play.api.mvc._
|
||||
import javax.inject.*
|
||||
import play.api.*
|
||||
import play.api.mvc.*
|
||||
import authentication.{AuthenticatedAction, AuthenticatedRequest}
|
||||
import caching.{Etagable, checkFresh}
|
||||
import models.User
|
||||
import play.api.libs.json.Json
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.charset.StandardCharsets.UTF_8
|
||||
import java.security.MessageDigest
|
||||
import java.util.Base64
|
||||
|
||||
/**
|
||||
* This controller creates an `Action` to handle HTTP requests to the
|
||||
* application's home page.
|
||||
*/
|
||||
@Singleton
|
||||
class HomeController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {
|
||||
class HomeController @Inject()(val controllerComponents: ControllerComponents,
|
||||
authenticatedAction: AuthenticatedAction)
|
||||
extends BaseController with Logging {
|
||||
|
||||
/**
|
||||
* Create an Action to render an HTML page.
|
||||
@@ -21,4 +32,26 @@ class HomeController @Inject()(val controllerComponents: ControllerComponents) e
|
||||
def index() = Action { implicit request: Request[AnyContent] =>
|
||||
Ok(views.html.index())
|
||||
}
|
||||
|
||||
def userData = authenticatedAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val user = request.user
|
||||
request.checkFresh(user) {
|
||||
// TODO: put the max-age somewhere into config
|
||||
Ok(Json.toJson(user)).withHeaders("Etag" -> user.etag, "Cache-Control" -> "private, max-age=15, must-revalidate")
|
||||
}
|
||||
}
|
||||
|
||||
private given Etagable[models.User] with {
|
||||
private val base64 = Base64.getUrlEncoder
|
||||
|
||||
extension (a: User)
|
||||
override def etag: String = {
|
||||
val md = MessageDigest.getInstance("SHA-1")
|
||||
md.update(ByteBuffer.allocate(8).putLong(a.id.getOrElse(0)))
|
||||
md.update(a.gid.getBytes(UTF_8))
|
||||
md.update(a.username.getBytes(UTF_8))
|
||||
|
||||
base64.encodeToString(md.digest())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package controllers.caching
|
||||
|
||||
import play.api.mvc.Results.NotModified
|
||||
import play.api.mvc.{Request, Result}
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
trait Etagable[A]:
|
||||
extension (a: A) def etag: String
|
||||
|
||||
|
||||
extension [A, R[X] <: Request[X], E: Etagable](request: R[A]) {
|
||||
def checkFreshAsync(entity: E)(onStale: => Future[Result])(using ec: ExecutionContext): Future[Result] = {
|
||||
compareEtag(entity).fold(onStale)(
|
||||
etag => Future.successful(NotModified.withHeaders("ETag" -> etag))
|
||||
)
|
||||
}
|
||||
|
||||
def checkFresh(entity: E)(onStale: => Result): Result = {
|
||||
compareEtag(entity).fold(onStale)(
|
||||
etag => NotModified.withHeaders("ETag" -> etag)
|
||||
)
|
||||
}
|
||||
|
||||
private def compareEtag(entity: E) = request.headers.get("If-None-Match").filter(e => e == entity.etag)
|
||||
}
|
||||
@@ -6,13 +6,11 @@ import java.time.Instant
|
||||
|
||||
case class User(id: Option[Long], username: String, gid: String, passwordDigest: Option[String])
|
||||
object User {
|
||||
given writes: Writes[User] = new Writes[User] {
|
||||
override def writes(o: User): JsValue = Json.obj(
|
||||
given writes: Writes[User] = (o: User) => Json.obj(
|
||||
"gid" -> o.gid,
|
||||
"username" -> o.username
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
case class AuthToken(id: Option[Long], userId: Long, token: String, createdAt: Instant, expiresAt: Instant)
|
||||
object AuthToken {
|
||||
|
||||
@@ -8,6 +8,7 @@ play.filters.csrf.header.bypassHeaders {
|
||||
}
|
||||
play.filters.cors {
|
||||
allowedOrigins = ["http://localhost:4321"]
|
||||
exposedHeaders = ["ETag"]
|
||||
}
|
||||
|
||||
play.modules.enabled += "mqttClient.Module"
|
||||
|
||||
@@ -9,6 +9,8 @@ GET / controllers.HomeController.index()
|
||||
POST /login controllers.authentication.SessionsController.login()
|
||||
DELETE /logout controllers.authentication.SessionsController.logout()
|
||||
|
||||
GET /user_data controllers.HomeController.userData()
|
||||
|
||||
# Map static resources from the /public folder to the /assets URL path
|
||||
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user