diff options
Diffstat (limited to 'src/main/scala/byspel/Ui.scala')
-rw-r--r-- | src/main/scala/byspel/Ui.scala | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/src/main/scala/byspel/Ui.scala b/src/main/scala/byspel/Ui.scala new file mode 100644 index 0000000..ac26164 --- /dev/null +++ b/src/main/scala/byspel/Ui.scala @@ -0,0 +1,119 @@ +package byspel + +import akka.http.scaladsl.server.Route +import akka.http.scaladsl.marshalling.{Marshaller, ToEntityMarshaller} +import akka.http.scaladsl.model.headers.HttpCookie +import akka.http.scaladsl.model.{MediaTypes, StatusCodes, Uri} +import app.HttpApi +import scalatags.Text.all._ + +trait Ui extends HttpApi { self: Service with Tables => + + // allows using scalatags templates as HTTP responses + implicit val tagMarshaller: ToEntityMarshaller[Tag] = { + Marshaller.stringMarshaller(MediaTypes.`text/html`).compose { (tag: Tag) => + tag.render + } + } + + def page(content: Tag*) = html( + scalatags.Text.all.head( + link( + rel := "stylesheet", + `type` := "text/css", + href := "/assets/normalize.css" + ), + link( + rel := "stylesheet", + `type` := "text/css", + href := "/assets/main.css" + ) + ), + body( + content + ) + ) + + def loginForm(alert: Option[String]) = page( + img(src := "/assets/logo.svg"), + h3("Sign in to crashbox"), + alert match { + case Some(message) => div(`class` := "alert")(message) + case None => span() + }, + form(action := "/login", attr("method") := "post")( + label(`for` := "username")("Username or email address"), + input(`type` := "text", placeholder := "", name := "username", required), + label(`for` := "password")("Password"), + input(`type` := "password", + placeholder := "", + name := "password", + required), + button(`type` := "submit")("Sign in") + ) + ) + + def mainPage(user: UsersRow) = page( + h1(s"Welcome ${user.fullName.getOrElse("")}!"), + form(action := "/logout", attr("method") := "post")( + button(`type` := "submit")("Sign out") + ) + ) + + def authenticated(inner: UsersRow => Route): Route = + optionalCookie("session") { + case Some(sessionCookie) => + onSuccess(self.checkSession(sessionCookie.value)) { + case Some(user) => + inner(user) + case None => complete(StatusCodes.NotFound) + } + case None => complete(StatusCodes.NotFound) + } + + def route = + pathPrefix("assets") { + getFromResourceDirectory("assets") + } ~ path("login") { + get { + complete(loginForm(None)) + } ~ + post { + formFields("username", "password") { + case (u, p) => + onSuccess(self.login(u, p)) { + case None => + complete(StatusCodes.NotFound -> loginForm( + Some("Incorrect username or password."))) + case Some((user, session)) => + setCookie(HttpCookie("session", session.sessionId)) { + redirect(Uri(s"/${user.primaryEmail}"), StatusCodes.Found) + } + } + } + } + } ~ path("logout") { + post { + cookie("session") { cookiePair => + onSuccess(endSession(cookiePair.value)) { _ => + deleteCookie(cookiePair.name) { + redirect(Uri("/"), StatusCodes.Found) + } + } + } + } + } ~ path(Segment) { userEmail => + authenticated { user => + if (user.primaryEmail == userEmail) { + get { + complete(mainPage(user)) + } + } else { + complete(StatusCodes.NotFound) + } + } + } ~ get { + redirect(Uri("/login"), StatusCodes.Found) + } + +} |