aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/byspel/Ui.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/byspel/Ui.scala')
-rw-r--r--src/main/scala/byspel/Ui.scala119
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)
+ }
+
+}