simplify and randomize assets versions - closes #4561

Requires replacing the following nginx config:

    rewrite ^/assets/\d+/(.*)$ /assets/$1;

with

    rewrite "^/assets/\w{6}/(.*)$" /assets/$1;
pull/4616/head
Thibault Duplessis 2018-09-09 15:53:22 +02:00
parent 8d0f9f7794
commit accb4b0cbd
15 changed files with 44 additions and 58 deletions

View File

@ -67,7 +67,6 @@ object Global extends GlobalSettings {
fuccess(InternalServerError(views.html.base.errorPage(ex) {
lila.api.Context.error(
req,
lila.common.AssetVersion(lila.app.Env.api.assetVersionSetting.get()),
lila.i18n.defaultLang,
HTTPRequest.isSynchronousHttp(req) option lila.common.Nonce.random
)

View File

@ -14,7 +14,6 @@ object Dev extends LilaController {
Env.security.ugcArmedSetting,
Env.security.emailBlacklistSetting,
Env.irwin.irwinModeSetting,
Env.api.assetVersionSetting,
Env.explorer.indexFlowSetting,
Env.report.scoreThresholdSetting,
Env.api.cspEnabledSetting

View File

@ -376,7 +376,7 @@ private[controllers] trait LilaController
private def pageDataBuilder(ctx: UserContext, hasFingerprint: Boolean): Fu[PageData] = {
val isPage = HTTPRequest isSynchronousHttp ctx.req
val nonce = isPage option Nonce.random
ctx.me.fold(fuccess(PageData.anon(ctx.req, getAssetVersion, nonce, blindMode(ctx)))) { me =>
ctx.me.fold(fuccess(PageData.anon(ctx.req, nonce, blindMode(ctx)))) { me =>
import lila.relation.actorApi.OnlineFriends
Env.pref.api.getPref(me, ctx.req) zip {
if (isPage) {
@ -394,15 +394,12 @@ private[controllers] trait LilaController
PageData(onlineFriends, teamNbRequests, nbChallenges, nbNotifications, pref,
blindMode = blindMode(ctx),
hasFingerprint = hasFingerprint,
assetVersion = getAssetVersion,
inquiry = inquiry,
nonce = nonce)
}
}
}
protected def getAssetVersion = lila.common.AssetVersion(Env.api.assetVersionSetting.get())
private def blindMode(implicit ctx: UserContext) =
ctx.req.cookies.get(Env.api.Accessibility.blindCookieName) ?? { c =>
c.value.nonEmpty && c.value == Env.api.Accessibility.hash

View File

@ -157,5 +157,5 @@ Disallow: /games/export
Ok(html.site.getFishnet()).fuccess
}
def versionedAsset(version: Int, file: String) = Assets.at(path = "/public", file)
def versionedAsset(version: String, file: String) = Assets.at(path = "/public", file)
}

View File

@ -96,8 +96,7 @@ object Tv extends LilaController {
case Some(game) => Ok(views.html.tv.embed(
Pov first game,
get("bg", req) | "light",
lila.pref.Theme(~get("theme", req)).cssClass,
assetVersion = getAssetVersion
lila.pref.Theme(~get("theme", req)).cssClass
))
}
}

View File

@ -17,44 +17,37 @@ trait AssetHelper { self: I18nHelper =>
val assetBaseUrl = s"//$assetDomain"
def assetUrl(path: String, version: AssetVersion): String =
s"$assetBaseUrl/assets/$version/$path"
def assetUrl(path: String)(implicit ctx: Context): String =
assetUrl(path, ctx.pageData.assetVersion)
def assetVersion = AssetVersion.current
def assetUrl(path: String): String =
s"$assetBaseUrl/assets/$assetVersion/$path"
def cdnUrl(path: String) = s"$assetBaseUrl$path"
def staticUrl(path: String) = s"$assetBaseUrl/assets/$path"
def dbImageUrl(path: String) = s"$assetBaseUrl/image/$path"
def cssTag(name: String)(implicit ctx: Context): Html =
cssAt("stylesheets/" + name)
def cssTag(name: String): Html = cssAt("stylesheets/" + name)
def cssVendorTag(name: String)(implicit ctx: Context) =
cssAt("vendor/" + name)
def cssVendorTag(name: String) = cssAt("vendor/" + name)
def cssAt(path: String, version: AssetVersion): Html = Html {
val href = assetUrl(path, version)
s"""<link href="$href" type="text/css" rel="stylesheet"/>"""
def cssAt(path: String): Html = Html {
s"""<link href="${assetUrl(path)}" type="text/css" rel="stylesheet"/>"""
}
def cssAt(path: String)(implicit ctx: Context): Html =
cssAt(path, ctx.pageData.assetVersion)
def jsTag(name: String, async: Boolean = false)(implicit ctx: Context) =
def jsTag(name: String, async: Boolean = false) =
jsAt("javascripts/" + name, async = async)
def jsAt(path: String, async: Boolean, version: AssetVersion): Html = Html {
val src = assetUrl(path, version)
def jsAt(path: String, async: Boolean = false): Html = Html {
val src = assetUrl(path)
s"""<script${if (async) " async defer" else ""} src="$src"></script>"""
}
def jsAt(path: String, async: Boolean = false)(implicit ctx: Context): Html =
jsAt(path, async, ctx.pageData.assetVersion)
val jQueryTag = Html {
s"""<script src="${staticUrl("javascripts/vendor/jquery.min.js")}"></script>"""
}
def roundTag(implicit ctx: Context) =
def roundTag =
jsAt(s"compiled/lichess.round${isProd ?? (".min")}.js", async = true)
val highchartsLatestTag = Html {

View File

@ -50,7 +50,7 @@ csp: Option[lila.common.ContentSecurityPolicy] = None)(body: Html)(implicit ctx:
@if(ctx.userContext.impersonatedBy.isDefined) { @cssTag("impersonate.css") }
@if(isStage) { @cssTag("stage.css") }
@moreCss
<link id="piece-sprite" href="@staticUrl(s"stylesheets/piece/${ctx.currentPieceSet}.css?v=${ctx.pageData.assetVersion}")" type="text/css" rel="stylesheet"/>
<link id="piece-sprite" href="@staticUrl(s"stylesheets/piece/${ctx.currentPieceSet}.css?v=$assetVersion")" type="text/css" rel="stylesheet"/>
<meta content="@openGraph.fold(trans.siteDescription.txt())(o => o.description)" name="description">
<link id="favicon" rel="shortcut icon" href="@staticUrl("images/favicon-32-white.png")" type="image/x-icon" />
<link rel="mask-icon" href="@staticUrl("favicon.svg")" color="black">
@ -84,7 +84,7 @@ csp: Option[lila.common.ContentSecurityPolicy] = None)(body: Html)(implicit ctx:
data-sound-set="@ctx.currentSoundSet"
data-socket-domain="@socketDomain"
data-asset-url="@assetBaseUrl"
data-asset-version="@ctx.pageData.assetVersion"
data-asset-version="@assetVersion"
@ctx.nonce.map { nonce => data-nonce="@nonce" }
@ctx.zoom.map { zoom => data-zoom="@zoom" }>
<form id="blind_mode" action="@routes.Main.toggleBlindMode" method="POST"><input type="hidden" name="enable" value="@if(ctx.blindMode){0}else{1}" /><input type="hidden" name="redirect" value="@ctx.req.path" /><button type="submit">Accessibility: @if(ctx.blindMode){Disable}else{Enable} blind mode</button></form>

View File

@ -45,7 +45,8 @@
@base.form.input(form("command"))
</form>
<h2>Command examples:</h2>
<pre>puzzle disable 70000
<pre>change asset version
puzzle disable 70000
team disable foobar
team enable foobar
fishnet client create {username} analysis

View File

@ -1,4 +1,4 @@
@(pov: Pov, bg: String, theme: String, assetVersion: lila.common.AssetVersion)(implicit req: RequestHeader)
@(pov: Pov, bg: String, theme: String)(implicit req: RequestHeader)
<!doctype html>
<html>
@ -7,11 +7,11 @@
<meta name="Content-Security-Policy" content="@basicCsp">
<title>lichess.org TV</title>
@if(bg == "dark") {
@cssAt("stylesheets/dark.css", assetVersion)
@cssAt("stylesheets/dark.css")
}
@cssAt(s"stylesheets/piece/merida.css", assetVersion)
@cssAt("stylesheets/common.css", assetVersion)
@cssAt("stylesheets/board.css", assetVersion)
@cssAt(s"stylesheets/piece/merida.css")
@cssAt("stylesheets/common.css")
@cssAt("stylesheets/board.css")
</head>
<body
class="base highlight @bg"
@ -22,7 +22,7 @@
@game.vstext(pov)(none)
</div>
@jQueryTag
@jsAt("javascripts/vendor/chessground.min.js", false, assetVersion)
@jsAt("compiled/tv.js", false, assetVersion)
@jsAt("javascripts/vendor/chessground.min.js", false)
@jsAt("compiled/tv.js", false)
</body>
</html>

View File

@ -9,10 +9,7 @@ net {
protocol = "http://"
base_url = ${net.protocol}${net.domain}
ip = "5.196.91.160"
asset {
domain = "lichess-assets.local"
version = 2108
}
asset.domain = "lichess-assets.local"
email = "contact@lichess.org"
crawlable = false
}

View File

@ -611,7 +611,7 @@ POST /jslog/$id<\w{12}> controllers.Main.jslog(id: String)
POST /jsmon/:event controllers.Main.jsmon(event: String)
# Assets
GET /assets/$version<\d+>/*file controllers.Main.versionedAsset(version: Int, file: String)
GET /assets/$version<\d+>/*file controllers.Main.versionedAsset(version: String, file: String)
GET /assets/*file controllers.Assets.at(path="/public", file)
GET /robots.txt controllers.Main.robots

View File

@ -18,6 +18,10 @@ private[api] final class Cli(bus: lila.common.Bus) extends lila.common.Cli {
case "uptime" :: Nil => fuccess(lila.common.PlayApp.uptime.toStandardSeconds.getSeconds.toString)
case "deploy" :: "pre" :: Nil => remindDeploy(lila.hub.actorApi.DeployPre)
case "deploy" :: "post" :: Nil => remindDeploy(lila.hub.actorApi.DeployPost)
case "change" :: ("asset" | "assets") :: "version" :: Nil =>
import lila.common.AssetVersion
AssetVersion.change
fuccess(s"Changed to ${AssetVersion.current}")
case "gdpr" :: "erase" :: username :: "forever" :: Nil =>
lila.user.UserRepo named username flatMap {
case None => fuccess("No such user.")

View File

@ -3,7 +3,7 @@ package lila.api
import play.api.mvc.RequestHeader
import play.api.i18n.Lang
import lila.common.{ HTTPRequest, AssetVersion, Nonce }
import lila.common.{ HTTPRequest, Nonce }
import lila.pref.Pref
import lila.relation.actorApi.OnlineFriends
import lila.user.{ UserContext, HeaderUserContext, BodyUserContext }
@ -16,7 +16,6 @@ case class PageData(
pref: Pref,
blindMode: Boolean,
hasFingerprint: Boolean,
assetVersion: AssetVersion,
inquiry: Option[lila.mod.Inquiry],
nonce: Option[Nonce],
error: Boolean = false
@ -24,7 +23,7 @@ case class PageData(
object PageData {
def anon(req: RequestHeader, v: AssetVersion, nonce: Option[Nonce], blindMode: Boolean = false) = PageData(
def anon(req: RequestHeader, nonce: Option[Nonce], blindMode: Boolean = false) = PageData(
OnlineFriends.empty,
teamNbRequests = 0,
nbChallenges = 0,
@ -32,12 +31,11 @@ object PageData {
lila.pref.RequestPref fromRequest req,
blindMode = blindMode,
hasFingerprint = false,
assetVersion = v,
inquiry = none,
nonce = nonce
)
def error(req: RequestHeader, v: AssetVersion, nonce: Option[Nonce]) = anon(req, v, nonce).copy(error = true)
def error(req: RequestHeader, nonce: Option[Nonce]) = anon(req, nonce).copy(error = true)
}
sealed trait Context extends lila.user.UserContextWrapper {
@ -101,8 +99,8 @@ final class HeaderContext(
object Context {
def error(req: RequestHeader, v: AssetVersion, lang: Lang, nonce: Option[Nonce]): HeaderContext =
new HeaderContext(UserContext(req, none, none, lang), PageData.error(req, v, nonce))
def error(req: RequestHeader, lang: Lang, nonce: Option[Nonce]): HeaderContext =
new HeaderContext(UserContext(req, none, none, lang), PageData.error(req, nonce))
def apply(userContext: HeaderUserContext, pageData: PageData): HeaderContext =
new HeaderContext(userContext, pageData)

View File

@ -60,13 +60,6 @@ final class Env(
private val InfluxEventEndpoint = config getString "api.influx_event.endpoint"
private val InfluxEventEnv = config getString "api.influx_event.env"
val assetVersionSetting = settingStore[Int](
"assetVersion",
default = config getInt "net.asset.version",
text = "Assets version. Increment to force all clients to load a new version of static assets. Decrement to serve a previous revision of static assets.".some,
init = (config, db) => config.value max db.value
)
val cspEnabledSetting = settingStore[Boolean](
"cspEnabled",
default = true,

View File

@ -6,7 +6,13 @@ case class ApiVersion(value: Int) extends AnyVal with IntValue {
def v3 = value == 3
}
case class AssetVersion(value: Int) extends AnyVal with IntValue
case class AssetVersion(value: String) extends AnyVal with StringValue
object AssetVersion {
var current = random
def change = { current = random }
private def random = AssetVersion(ornicar.scalalib.Random nextString 6)
}
case class MaxPerPage(value: Int) extends AnyVal with IntValue