improve, compress and sign the javascript
parent
af563653bd
commit
110e2a871d
|
@ -4,6 +4,7 @@ logs
|
|||
project/project
|
||||
project/target
|
||||
public/trans
|
||||
public/compiled
|
||||
serve/
|
||||
serve/README
|
||||
target
|
||||
|
|
|
@ -83,6 +83,9 @@ trait LilaController
|
|||
|
||||
protected def JsonIOk(map: IO[Map[String, Any]]) = JsonOk(map.unsafePerformIO)
|
||||
|
||||
protected def JsIOk(js: IO[String], headers: (String, String)*) =
|
||||
Ok(js.unsafePerformIO) as JAVASCRIPT withHeaders (headers: _*)
|
||||
|
||||
protected def ValidOk(valid: Valid[Unit]) = valid.fold(
|
||||
e ⇒ BadRequest(e.shows),
|
||||
_ ⇒ Ok("ok")
|
||||
|
|
|
@ -24,6 +24,7 @@ object Round extends LilaController with TheftPrevention with RoundEventPerforme
|
|||
private def userRepo = env.user.userRepo
|
||||
private def analyser = env.analyse.analyser
|
||||
private def tournamentRepo = env.tournament.repo
|
||||
private def gameJs = env.game.gameJs
|
||||
|
||||
def websocketWatcher(gameId: String, color: String) = WebSocket.async[JsValue] { req ⇒
|
||||
implicit val ctx = reqToCtx(req)
|
||||
|
@ -32,16 +33,22 @@ object Round extends LilaController with TheftPrevention with RoundEventPerforme
|
|||
color,
|
||||
getInt("version"),
|
||||
get("sri"),
|
||||
get("tk"),
|
||||
ctx).unsafePerformIO
|
||||
}
|
||||
|
||||
def websocketPlayer(fullId: String) = WebSocket.async[JsValue] { req ⇒
|
||||
implicit val ctx = reqToCtx(req)
|
||||
socket.joinPlayer(
|
||||
fullId,
|
||||
getInt("version"),
|
||||
get("sri"),
|
||||
ctx).unsafePerformIO
|
||||
socket.joinPlayer(
|
||||
fullId,
|
||||
getInt("version"),
|
||||
get("sri"),
|
||||
get("tk"),
|
||||
ctx).unsafePerformIO
|
||||
}
|
||||
|
||||
def signedJs(gameId: String) = Open { implicit ctx ⇒
|
||||
JsIOk(gameRepo token gameId map gameJs.sign, CACHE_CONTROL -> "max-age=3600")
|
||||
}
|
||||
|
||||
def player(fullId: String) = Open { implicit ctx ⇒
|
||||
|
|
|
@ -25,6 +25,7 @@ final class Settings(config: Config, val IsDev: Boolean) {
|
|||
val GamePaginatorMaxPerPage = getInt("game.paginator.max_per_page")
|
||||
val GameCollectionGame = getString("game.collection.game")
|
||||
val GameCollectionPgn = getString("game.collection.pgn")
|
||||
val GameJsPath = getString("game.js_path")
|
||||
|
||||
val SearchESHost = getString("search.elasticsearch.host")
|
||||
val SearchESPort = getInt("search.elasticsearch.port")
|
||||
|
|
|
@ -15,6 +15,7 @@ import scala.math.min
|
|||
|
||||
case class DbGame(
|
||||
id: String,
|
||||
token: String,
|
||||
whitePlayer: DbPlayer,
|
||||
blackPlayer: DbPlayer,
|
||||
status: Status,
|
||||
|
@ -322,6 +323,7 @@ case class DbGame(
|
|||
|
||||
def encode = RawDbGame(
|
||||
id = id,
|
||||
tk = token.some filter (DbGame.defaultToken !=),
|
||||
p = players map (_.encode),
|
||||
s = status.id,
|
||||
t = turns,
|
||||
|
@ -366,6 +368,8 @@ object DbGame {
|
|||
val gameIdSize = 8
|
||||
val playerIdSize = 4
|
||||
val fullIdSize = 12
|
||||
val tokenSize = 4
|
||||
val defaultToken = "-tk-"
|
||||
|
||||
def abandonedDate = DateTime.now - 10.days
|
||||
|
||||
|
@ -380,6 +384,7 @@ object DbGame {
|
|||
mode: Mode,
|
||||
variant: Variant): DbGame = DbGame(
|
||||
id = IdGenerator.game,
|
||||
token = IdGenerator.token,
|
||||
whitePlayer = whitePlayer withEncodedPieces game.allPieces,
|
||||
blackPlayer = blackPlayer withEncodedPieces game.allPieces,
|
||||
status = Status.Created,
|
||||
|
@ -398,6 +403,7 @@ object DbGame {
|
|||
|
||||
case class RawDbGame(
|
||||
@Key("_id") id: String,
|
||||
tk: Option[String] = None,
|
||||
p: List[RawDbPlayer],
|
||||
s: Int,
|
||||
t: Int,
|
||||
|
@ -423,6 +429,7 @@ case class RawDbGame(
|
|||
trueStatus ← Status(s)
|
||||
} yield DbGame(
|
||||
id = id,
|
||||
token = tk | DbGame.defaultToken,
|
||||
whitePlayer = whitePlayer,
|
||||
blackPlayer = blackPlayer,
|
||||
status = trueStatus,
|
||||
|
|
|
@ -33,4 +33,6 @@ final class GameEnv(
|
|||
lazy val listMenu = ListMenu(cached) _
|
||||
|
||||
lazy val rewind = new Rewind
|
||||
|
||||
lazy val gameJs = new GameJs(settings.GameJsPath)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package lila
|
||||
package game
|
||||
|
||||
final class GameJs(path: String) {
|
||||
|
||||
lazy val unsigned: String = {
|
||||
val source = scala.io.Source fromFile path
|
||||
source.mkString ~ { _ => source.close }
|
||||
}
|
||||
|
||||
val placeholder = "--tkph--"
|
||||
|
||||
def sign(token: String) = unsigned.replace(placeholder, token)
|
||||
}
|
|
@ -48,6 +48,10 @@ final class GameRepo(collection: MongoCollection)
|
|||
|
||||
def pov(ref: PovRef): IO[Option[Pov]] = pov(ref.gameId, ref.color)
|
||||
|
||||
def token(id: String): IO[String] = io {
|
||||
primitiveProjection[String](idSelector(id), "tk") | DbGame.defaultToken
|
||||
}
|
||||
|
||||
def save(game: DbGame): IO[Unit] = io {
|
||||
update(idSelector(game), _grater asDBObject game.encode)
|
||||
}
|
||||
|
|
|
@ -7,5 +7,7 @@ object IdGenerator {
|
|||
|
||||
def game = Random nextString DbGame.gameIdSize
|
||||
|
||||
def token = Random nextString DbGame.tokenSize
|
||||
|
||||
def player = Random nextString DbGame.playerIdSize
|
||||
}
|
||||
|
|
|
@ -112,15 +112,17 @@ final class Socket(
|
|||
colorName: String,
|
||||
version: Option[Int],
|
||||
uid: Option[String],
|
||||
token: Option[String],
|
||||
ctx: Context): IO[SocketPromise] =
|
||||
getWatcherPov(gameId, colorName) map { join(_, false, version, uid, ctx) }
|
||||
getWatcherPov(gameId, colorName) map { join(_, false, version, uid, token, ctx) }
|
||||
|
||||
def joinPlayer(
|
||||
fullId: String,
|
||||
version: Option[Int],
|
||||
uid: Option[String],
|
||||
token: Option[String],
|
||||
ctx: Context): IO[SocketPromise] =
|
||||
getPlayerPov(fullId) map { join(_, true, version, uid, ctx) }
|
||||
getPlayerPov(fullId) map { join(_, true, version, uid, token, ctx) }
|
||||
|
||||
private def parseMove(event: JsValue) = for {
|
||||
d ← event obj "d"
|
||||
|
@ -136,9 +138,10 @@ final class Socket(
|
|||
owner: Boolean,
|
||||
versionOption: Option[Int],
|
||||
uidOption: Option[String],
|
||||
tokenOption: Option[String],
|
||||
ctx: Context): SocketPromise =
|
||||
((povOption |@| uidOption |@| versionOption) apply {
|
||||
(pov: Pov, uid: String, version: Int) ⇒
|
||||
((povOption |@| uidOption |@| tokenOption |@| versionOption) apply {
|
||||
(pov: Pov, uid: String, token: String, version: Int) ⇒
|
||||
(for {
|
||||
hub ← hubMaster ? GetHub(pov.gameId) mapTo manifest[ActorRef]
|
||||
socket ← hub ? Join(
|
||||
|
@ -146,10 +149,12 @@ final class Socket(
|
|||
user = ctx.me,
|
||||
version = version,
|
||||
color = pov.color,
|
||||
owner = owner
|
||||
owner = owner && token == pov.game.token
|
||||
) map {
|
||||
case Connected(enumerator, member) ⇒ {
|
||||
if (owner && !member.owner) println("Websocket hijacking detected (%s) %s".format(pov.gameId, ctx.toString))
|
||||
if (owner && !member.owner) {
|
||||
println("Websocket hijacking detected %s %s".format(pov.gameId, ctx.toString))
|
||||
}
|
||||
(Iteratee.foreach[JsValue](
|
||||
controller(hub, uid, member, PovRef(pov.gameId, member.color))
|
||||
) mapDone { _ ⇒
|
||||
|
|
|
@ -7,7 +7,7 @@ import play.api.templates.Html
|
|||
|
||||
trait AssetHelper {
|
||||
|
||||
val assetVersion = 4
|
||||
val assetVersion = 6
|
||||
|
||||
def cssTag(name: String) = css("stylesheets/" + name)
|
||||
|
||||
|
@ -20,10 +20,13 @@ trait AssetHelper {
|
|||
|
||||
def jsTag(name: String) = js("javascripts/" + name)
|
||||
|
||||
def jsTagC(name: String) = js("compiled/" + name)
|
||||
|
||||
def jsVendorTag(name: String) = js("vendor/" + name)
|
||||
|
||||
def js(path: String) = Html {
|
||||
"""<script src="%s?v=%d"></script>"""
|
||||
.format(routes.Assets.at(path), assetVersion)
|
||||
private def js(path: String) = jsAt(routes.Assets.at(path).toString)
|
||||
|
||||
def jsAt(path: String) = Html {
|
||||
"""<script src="%s?v=%d"></script>""".format(path, assetVersion)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@(title: String, active: Option[ui.SiteMenu.Elem] = None, baseline: Option[Html] = None, goodies: Option[Html] = None, menu: Option[Html] = None, chat: Option[Html] = None, underchat: Option[Html] = None, robots: Boolean = true, moreCss: Html = Html(""), moreJs: Html = Html(""))(body: Html)(implicit ctx: Context)
|
||||
@(title: String, active: Option[ui.SiteMenu.Elem] = None, baseline: Option[Html] = None, goodies: Option[Html] = None, menu: Option[Html] = None, chat: Option[Html] = None, underchat: Option[Html] = None, robots: Boolean = true, moreCss: Html = Html(""), moreJs: Html = Html(""), signedJs: Option[String] = None)(body: Html)(implicit ctx: Context)
|
||||
|
||||
<!doctype html>
|
||||
<html lang="@lang.language">
|
||||
|
@ -105,7 +105,7 @@
|
|||
</div>
|
||||
</div>
|
||||
@jsTag("deps.min.js")
|
||||
@jsTag("big.js")
|
||||
@signedJs.fold(jsAt, jsTagC("big.js"))
|
||||
@moreJs
|
||||
@if(lang.language != "en") {
|
||||
<script src="@routes.Assets.at("trans/" + lang.language + ".js")?v=@assetVersion"></script>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
@(title: String, goodies: Html, chat: Option[Html] = None, underchat: Option[Html] = None, robots: Boolean = true)(body: Html)(implicit ctx: Context)
|
||||
@(title: String, goodies: Html, chat: Option[Html] = None, underchat: Option[Html] = None, robots: Boolean = true, signedJs: Option[String] = None)(body: Html)(implicit ctx: Context)
|
||||
|
||||
@base.layout(
|
||||
title = title,
|
||||
goodies = goodies.some,
|
||||
active = siteMenu.play.some,
|
||||
chat = chat,
|
||||
underchat = underchat,
|
||||
robots = robots)(body)
|
||||
robots = robots,
|
||||
signedJs = signedJs)(body)
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
title = title,
|
||||
goodies = views.html.game.infoBox(pov, tour),
|
||||
chat = roomHtml.map(round.room(_, false)),
|
||||
underchat = underchat.some) {
|
||||
underchat = underchat.some,
|
||||
signedJs = routes.Round.signedJs(pov.gameId).toString.some) {
|
||||
<div class="lichess_game clearfix lichess_player_@color not_spectator"
|
||||
data-socket-url="@routes.Round.websocketPlayer(fullId)"
|
||||
data-table-url="@routes.Round.tablePlayer(fullId)"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
. bin/lilarc
|
||||
|
||||
lilalog "Compiling javascript"
|
||||
|
||||
mkdir -p public/compiled
|
||||
closure --js public/javascripts/big.js --js_output_file public/compiled/big.js
|
|
@ -11,6 +11,7 @@ if [ -z $1 ]; then
|
|||
elif [ $1 = "main" ]; then
|
||||
REMOTE="balrog"
|
||||
REMOTE_DIR="/home/lila"
|
||||
bin/closure
|
||||
elif [ $1 = "ai" ]; then
|
||||
REMOTE="marty"
|
||||
REMOTE_DIR="/home/lila3"
|
||||
|
|
|
@ -17,6 +17,7 @@ GET /$gameId<[\w\-]{8}> controllers.Round.watcher(g
|
|||
GET /$gameId<[\w\-]{8}>/$color<white|black> controllers.Round.watcher(gameId: String, color: String)
|
||||
GET /$fullId<[\w\-]{12}> controllers.Round.player(fullId: String)
|
||||
GET /$gameId<[\w\-]{8}>/$color<white|black>/socket controllers.Round.websocketWatcher(gameId: String, color: String)
|
||||
GET /$gameId<[\w\-]{8}>/signed.js controllers.Round.signedJs(gameId: String)
|
||||
GET /$fullId<[\w\-]{12}>/socket controllers.Round.websocketPlayer(fullId: String)
|
||||
GET /$fullId<[\w\-]{12}>/abort controllers.Round.abort(fullId: String)
|
||||
GET /$fullId<[\w\-]{12}>/resign controllers.Round.resign(fullId: String)
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// ==ClosureCompiler==
|
||||
// @compilation_level ADVANCED_OPTIMIZATIONS
|
||||
// @externs_url http://closure-compiler.googlecode.com/svn/trunk/contrib/externs/jquery-1.7.js
|
||||
// ==/ClosureCompiler==
|
||||
if (typeof console == "undefined" || typeof console.log == "undefined") console = {
|
||||
log: function() {}
|
||||
};
|
||||
|
@ -512,7 +516,7 @@ $.widget("lichess.game", {
|
|||
self.options.tableUrl = self.element.data('table-url');
|
||||
self.options.playersUrl = self.element.data('players-url');
|
||||
self.options.socketUrl = self.element.data('socket-url');
|
||||
self.socketAckTimeout;
|
||||
self.socketAckTimeout = null;
|
||||
|
||||
$("div.game_tournament .clock").each(function() {
|
||||
$(this).clock({time: $(this).data("time")}).clock("start");
|
||||
|
@ -567,6 +571,7 @@ $.widget("lichess.game", {
|
|||
options: {
|
||||
name: "game"
|
||||
},
|
||||
params: { tk: "--tkph--"},
|
||||
events: {
|
||||
ack: function() {
|
||||
clearTimeout(self.socketAckTimeout);
|
||||
|
@ -871,12 +876,12 @@ $.widget("lichess.game", {
|
|||
moveData.promotion = "queen";
|
||||
sendMoveRequest(moveData);
|
||||
} else {
|
||||
var $choices = $('<div class="lichess_promotion_choice">').appendTo(self.$board).html('\
|
||||
<div data-piece="queen" class="lichess_piece queen ' + color + '"></div>\
|
||||
<div data-piece="knight" class="lichess_piece knight ' + color + '"></div>\
|
||||
<div data-piece="rook" class="lichess_piece rook ' + color + '"></div>\
|
||||
<div data-piece="bishop" class="lichess_piece bishop ' + color + '"></div>').fadeIn(self.options.animation_delay).find('div.lichess_piece').click(function() {
|
||||
moveData.promotion = $(this).attr('data-piece');
|
||||
var $choices = $('<div class="lichess_promotion_choice">')
|
||||
.appendTo(self.$board)
|
||||
.html('<div data-piece="queen" class="lichess_piece queen ' + color + '"></div><div data-piece="knight" class="lichess_piece knight ' + color + '"></div><div data-piece="rook" class="lichess_piece rook ' + color + '"></div><div data-piece="bishop" class="lichess_piece bishop ' + color + '"></div>')
|
||||
.fadeIn(self.options.animation_delay)
|
||||
.find('div.lichess_piece')
|
||||
.click(function() { moveData.promotion = $(this).attr('data-piece');
|
||||
sendMoveRequest(moveData);
|
||||
$choices.fadeOut(self.options.animation_delay, function() {
|
||||
$choices.remove();
|
||||
|
@ -1169,7 +1174,7 @@ $.widget("lichess.chat", {
|
|||
},
|
||||
append: function(msg) {
|
||||
var self = this;
|
||||
self.$msgs.append(urlToLink(msg))[0];
|
||||
self.$msgs.append(urlToLink(msg));
|
||||
$('body').trigger('lichess.content_loaded');
|
||||
self.$msgs[0].scrollTop = 9999999;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue