From c6ca57b8ef3e9d5cc0dfdc9a26c970533486c9cd Mon Sep 17 00:00:00 2001 From: Michael Skogberg Date: Tue, 10 Dec 2024 20:39:16 +0200 Subject: [PATCH] Fix table render --- .../scala/org/musicpimp/js/BaseSocket.scala | 16 ++++----- .../malliina/musicpimp/app/AppLoader.scala | 2 +- .../com/malliina/pimpcloud/CloudLoader.scala | 17 ++++++++-- .../app/controllers/pimpcloud/servers.scala | 33 ++++++++++--------- .../com/malliina/pimpcloud/js/AdminJS.scala | 20 +++++++---- .../com/malliina/pimpcloud/js/LogsJS.scala | 19 +++++------ 6 files changed, 62 insertions(+), 45 deletions(-) diff --git a/cross/js/src/main/scala/org/musicpimp/js/BaseSocket.scala b/cross/js/src/main/scala/org/musicpimp/js/BaseSocket.scala index 510dbc1a..84bd6de9 100644 --- a/cross/js/src/main/scala/org/musicpimp/js/BaseSocket.scala +++ b/cross/js/src/main/scala/org/musicpimp/js/BaseSocket.scala @@ -7,10 +7,10 @@ import org.scalajs.dom import org.scalajs.dom.{CloseEvent, Event, MessageEvent} class BaseSocket(wsPath: String, val hideClass: String, val log: BaseLogger) extends ScriptHelpers: - val okStatus = elem(OkStatus) - val failStatus = elem(FailStatus) + private val okStatus = elem(OkStatus) + private val failStatus = elem(FailStatus) - val socket: dom.WebSocket = openSocket(wsPath) + private val socket: dom.WebSocket = openSocket(wsPath) def handlePayload(payload: Json): Unit = () @@ -53,11 +53,11 @@ class BaseSocket(wsPath: String, val hideClass: String, val log: BaseLogger) ext def onConnected(e: Event): Unit = showConnected() - def onClosed(e: CloseEvent): Unit = showDisconnected() + private def onClosed(e: CloseEvent): Unit = showDisconnected() - def onError(e: Event): Unit = showDisconnected() + private def onError(e: Event): Unit = showDisconnected() - def openSocket(pathAndQuery: String) = + private def openSocket(pathAndQuery: String) = val wsUrl = s"$wsBaseUrl$pathAndQuery" val socket = new dom.WebSocket(wsUrl) socket.onopen = (e: Event) => onConnected(e) @@ -66,12 +66,12 @@ class BaseSocket(wsPath: String, val hideClass: String, val log: BaseLogger) ext socket.onerror = (e: Event) => onError(e) socket - def wsBaseUrl = + private def wsBaseUrl = val location = dom.window.location val wsProto = if location.protocol == "http:" then "ws" else "wss" s"$wsProto://${location.host}" - def onJsonException(t: Throwable): Unit = + private def onJsonException(t: Throwable): Unit = log.error(t) protected def onJsonFailure(result: String): Unit = diff --git a/musicpimp/app/com/malliina/musicpimp/app/AppLoader.scala b/musicpimp/app/com/malliina/musicpimp/app/AppLoader.scala index ef31e4c2..5d6240fe 100644 --- a/musicpimp/app/com/malliina/musicpimp/app/AppLoader.scala +++ b/musicpimp/app/com/malliina/musicpimp/app/AppLoader.scala @@ -48,7 +48,7 @@ case class InitOptions( users: Boolean = true, indexer: Boolean = true, cloud: Boolean = true, - cloudUri: FullUrl = CloudSocket.prodUri, + cloudUri: FullUrl = CloudSocket.devUri, useTray: Boolean = true ) diff --git a/pimpcloud/app/com/malliina/pimpcloud/CloudLoader.scala b/pimpcloud/app/com/malliina/pimpcloud/CloudLoader.scala index 51c057bd..f217588c 100644 --- a/pimpcloud/app/com/malliina/pimpcloud/CloudLoader.scala +++ b/pimpcloud/app/com/malliina/pimpcloud/CloudLoader.scala @@ -10,6 +10,7 @@ import com.malliina.oauth.GoogleOAuthCredentials import com.malliina.pimpcloud.CloudComponents.log import com.malliina.pimpcloud.ws.JoinedSockets import com.malliina.play.ActorExecution +import com.typesafe.config.ConfigFactory import controllers.pimpcloud.* import controllers.{Assets, AssetsComponents} import play.api.ApplicationLoader.Context @@ -21,8 +22,14 @@ import play.filters.gzip.GzipFilter import play.filters.headers.SecurityHeadersConfig import play.filters.hosts.AllowedHostsConfig +import java.nio.file.Paths import scala.concurrent.{ExecutionContextExecutor, Future} +object LocalConf: + val userHome = Paths.get(sys.props("user.home")) + val localConfFile = userHome.resolve(".pimpcloud/pimpcloud.conf") + val localConf = Configuration(ConfigFactory.parseFile(localConfFile.toFile)) + case class AppConf( pusher: (Configuration, OkClient) => Pusher, conf: Configuration => GoogleOAuthCredentials, @@ -84,9 +91,12 @@ class CloudComponents(context: Context, conf: AppConf) ) override lazy val allowedHostsConfig = AllowedHostsConfig(Seq("cloud.musicpimp.org", "localhost")) + override lazy val configuration: Configuration = + LocalConf.localConf.withFallback(context.initialConfiguration) + val defaultHttpConf = HttpConfiguration.fromConfiguration(configuration, environment) // Sets sameSite = None, otherwise the Google auth redirect will wipe out the session state - override lazy val httpConfiguration = + override lazy val httpConfiguration: HttpConfiguration = defaultHttpConf.copy( session = defaultHttpConf.session.copy(cookieName = "cloudSession", sameSite = None) ) @@ -96,7 +106,8 @@ class CloudComponents(context: Context, conf: AppConf) // Components override lazy val httpFilters: Seq[EssentialFilter] = Seq(securityHeadersFilter, new GzipFilter()) - private val adminAuth = new AdminOAuth(defaultActionBuilder, conf.conf(configuration)) + val google = conf.conf(configuration) + private val adminAuth = new AdminOAuth(defaultActionBuilder, google) val pimpAuth: PimpAuth = conf.pimpAuth(adminAuth, materializer) val http = OkClient.default @@ -114,7 +125,7 @@ class CloudComponents(context: Context, conf: AppConf) lazy val as = new Assets(httpErrorHandler, assetsMetadata, environment) lazy val router = new Routes(httpErrorHandler, p, w, push, joined, sc, l, adminAuth, joined.us, as) - log.info(s"Started pimpcloud ${BuildInfo.version}") + log.info(s"Started pimpcloud ${BuildInfo.version} with Google client ID '${google.clientId}'.") applicationLifecycle.addStopHook(() => Future.successful: diff --git a/pimpcloud/app/controllers/pimpcloud/servers.scala b/pimpcloud/app/controllers/pimpcloud/servers.scala index 8f912212..b14e63bd 100644 --- a/pimpcloud/app/controllers/pimpcloud/servers.scala +++ b/pimpcloud/app/controllers/pimpcloud/servers.scala @@ -40,23 +40,24 @@ class Servers(phoneMediator: ActorRef, val ctx: ActorExecution, errorHandler: Ht * auth failure or success */ def authAsync(rh: RequestHeader): Future[Either[AuthFailure, AuthedRequest]] = - Auth.basicCredentials(rh) map { creds => - if creds.password == Constants.pass then - val user = creds.username - val cloudId = if user.name.trim.nonEmpty then CloudID(user.name) else newID() - isConnected(cloudId) map { connected => - if connected then - log warn s"Unable to register client: '$cloudId'. Another client with that ID is already connected." - Left(InvalidCredentials(rh)) - else Right(AuthedRequest(Username(cloudId.id), rh)) - } - else fut(Left(InvalidCredentials(rh))) - } getOrElse { - log warn s"No credentials for request from '${Proxies.realAddress(rh)}'." - fut(Left(MissingCredentials(rh))) - } + Auth + .basicCredentials(rh) + .map: creds => + if creds.password == Constants.pass then + val user = creds.username + val cloudId = if user.name.trim.nonEmpty then CloudID(user.name) else newID() + isConnected(cloudId) map { connected => + if connected then + log warn s"Unable to register client: '$cloudId'. Another client with that ID is already connected." + Left(InvalidCredentials(rh)) + else Right(AuthedRequest(Username(cloudId.id), rh)) + } + else fut(Left(InvalidCredentials(rh))) + .getOrElse: + log warn s"No credentials for request from '${Proxies.realAddress(rh)}'." + fut(Left(MissingCredentials(rh))) - def newID(): CloudID = CloudID(UUID.randomUUID().toString take 5) + private def newID(): CloudID = CloudID(UUID.randomUUID().toString take 5) def fut[T](t: T): Future[T] = Future.successful(t) diff --git a/pimpcloud/frontend/src/main/scala/com/malliina/pimpcloud/js/AdminJS.scala b/pimpcloud/frontend/src/main/scala/com/malliina/pimpcloud/js/AdminJS.scala index 3d0f7e1c..ecf0b56e 100644 --- a/pimpcloud/frontend/src/main/scala/com/malliina/pimpcloud/js/AdminJS.scala +++ b/pimpcloud/frontend/src/main/scala/com/malliina/pimpcloud/js/AdminJS.scala @@ -1,10 +1,11 @@ package com.malliina.pimpcloud.js -import com.malliina.pimpcloud.CloudStrings.* import com.malliina.pimpcloud.* +import com.malliina.pimpcloud.CloudStrings.* import io.circe.Json -import org.scalajs.dom.{Element, HTMLTableElement} -import scalatags.Text.all.* +import org.scalajs.dom.{HTMLTableElement, html} +import scalatags.JsDom +import scalatags.JsDom.all.* class AdminJS extends SocketJS("/admin/usage"): val phonesTable = elemAs[HTMLTableElement](PhonesTableId) @@ -28,18 +29,23 @@ class AdminJS extends SocketJS("/admin/usage"): clearAndSet(requestsTable, requests, row) - def updatePhones(phones: Seq[PimpPhone]): Unit = + private def updatePhones(phones: Seq[PimpPhone]): Unit = def row(phone: PimpPhone) = tr(td(phone.s.id), td(phone.address)) clearAndSet(phonesTable, phones, row) - def updateServers(servers: Seq[PimpServer]): Unit = + private def updateServers(servers: Seq[PimpServer]): Unit = def row(server: PimpServer) = tr(td(server.id.id), td(server.address)) clearAndSet(serversTable, servers, row) - def clearAndSet[T](table: HTMLTableElement, es: Seq[T], toRow: T => Modifier): Unit = + private def clearAndSet[T]( + table: HTMLTableElement, + es: Seq[T], + toRow: T => JsDom.TypedTag[html.TableRow] + ): Unit = val rows = table.rows.size (1 to rows).foreach: _ => table.deleteRow(0) - es foreach { e => table.append(toRow(e).toString) } + es.foreach: e => + table.append(toRow(e).render) diff --git a/pimpcloud/frontend/src/main/scala/com/malliina/pimpcloud/js/LogsJS.scala b/pimpcloud/frontend/src/main/scala/com/malliina/pimpcloud/js/LogsJS.scala index bd1b069a..c552bed9 100644 --- a/pimpcloud/frontend/src/main/scala/com/malliina/pimpcloud/js/LogsJS.scala +++ b/pimpcloud/frontend/src/main/scala/com/malliina/pimpcloud/js/LogsJS.scala @@ -3,7 +3,7 @@ package com.malliina.pimpcloud.js import com.malliina.musicpimp.js.FrontStrings.LogTableBodyId import com.malliina.pimpcloud.js.LogsJS.randomString import io.circe.{Codec, Json} -import scalatags.Text.all.* +import scalatags.JsDom.all.* import org.scalajs.dom.document case class JVMLogEntry( @@ -25,15 +25,15 @@ object LogsJS: .mkString class LogsJS extends SocketJS("/admin/ws?f=json"): - val CellContent = "cell-content" - val CellWide = "cell-wide" - val tableContent = elem(LogTableBodyId) + private val CellContent = "cell-content" + private val CellWide = "cell-wide" + private val tableContent = elem(LogTableBodyId) override def handlePayload(payload: Json): Unit = handleValidated[Seq[JVMLogEntry]](payload): logs => - logs foreach prepend + logs.foreach(prepend) - private def prepend(entry: JVMLogEntry) = + private def prepend(entry: JVMLogEntry): Unit = val rowClass = entry.level match case "ERROR" => "danger" case "WARN" => "warning" @@ -48,12 +48,11 @@ class LogsJS extends SocketJS("/admin/ws?f=json"): .map(_ => a(href := "#", id := linkId)(level)) .getOrElse(level) - entry.stackTrace foreach { stackTrace => + entry.stackTrace.foreach: stackTrace => val errorRow = tr(`class` := hideClass, id := s"$rowId")( td(colspan := "5")(pre(stackTrace)) ) - tableContent prepend errorRow.render - } + tableContent.prepend(errorRow.render) val row = tr(`class` := rowClass)( cell(entry.timeFormatted), td(`class` := s"$CellContent $CellWide", id := msgCellId)(entry.message), @@ -61,7 +60,7 @@ class LogsJS extends SocketJS("/admin/ws?f=json"): cell(entry.threadName), cell(levelCell) ) - tableContent prepend row.render + tableContent.prepend(row.render) elem(linkId).onClick(_ => toggle(rowId)) // toggles text wrapping for long texts when clicked elem(msgCellId).onClick: _ =>