Skip to content

Commit

Permalink
Merge branch 'master' into board-menu
Browse files Browse the repository at this point in the history
* master: (336 commits)
  scala tweaks
  New Crowdin updates (#12949)
  tweak audio context handling
  voice mic selector
  prevent lichess.storage race condition on init
  moveCtrl is available
  remove ui/voice `let moveCtrl` global
  move voice toggle code out of the event listener
  mic.ts: if the audio context is suspended, wait for user interaction
  use OpaqueInt+Int=OpaqueInt
  Use fullMoveNumber when writing played moves
  Revert RendererActor visibility
  make RoundSocket round robin listen to 16 channels
  scala tweaks
  fewer dom text nodes and more css tricks
  only bind one click for all top players of a team battle
  remove superfluous team battle tag attr
  help modal tweaks
  remove debugger stmt
  sbt 1.9.0
  ...
  • Loading branch information
ornicar committed Jun 4, 2023
2 parents d410b0d + a2aba17 commit 86a73f3
Show file tree
Hide file tree
Showing 853 changed files with 13,641 additions and 6,098 deletions.
3 changes: 3 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ eea12a89f8550d74518f19ec9cda404e3f73b3d0

# Scala Steward: Reformat with scalafmt 3.7.1
02bcdbe4a32467dcc9403e2787126caaa249278b

# Scala Steward: Reformat with scalafmt 3.7.4
84137814df3b827196e1438f3bc654c0dcc1c1f2
2 changes: 1 addition & 1 deletion .github/workflows/assets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
submodules: recursive
- uses: pnpm/action-setup@v2
with:
version: '8.1'
version: '8.6'
- run: git submodule absorbgitdirs
- uses: actions/setup-node@v3
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ jobs:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: '8.1'
version: '8.6'
- uses: actions/setup-node@v3
with:
node-version: '16'
node-version: '17'
cache: pnpm
- uses: github/codeql-action/init@v2
with:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,6 @@ RUNNING_PID
# IntelliJ auto-generated files
.idea

# Docker-compose
# Docker-compose / docker sbt
/docker-compose.yml
/?
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "3.7.3"
version = "3.7.4"
runner.dialect = scala3

align.preset = more
Expand Down
5 changes: 2 additions & 3 deletions app/controllers/Account.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,13 @@ final class Account(
negotiate(
html = notFound,
api = _ =>
env.pref.api.getPref(me) map { prefs =>
Ok {
env.pref.api.getPref(me).map { prefs =>
Ok:
import lila.pref.JsonView.given
lila.common.LightUser.lightUserWrites.writes(me.light) ++ Json.obj(
"coach" -> isGranted(_.Coach),
"prefs" -> prefs
)
}
}
)
}
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/Analyse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ final class Analyse(
}

def replay(pov: Pov, userTv: Option[lila.user.User])(using ctx: Context) =
if (HTTPRequest.isCrawler(ctx.req).yes) replayBot(pov)
if HTTPRequest.isCrawler(ctx.req).yes then replayBot(pov)
else
env.game.gameRepo initialFen pov.gameId flatMap { initialFen =>
gameC.preloadUsers(pov.game) >> RedirectAtFen(pov, initialFen) {
Expand Down
31 changes: 11 additions & 20 deletions app/controllers/Api.scala
Original file line number Diff line number Diff line change
Expand Up @@ -145,35 +145,26 @@ final class Api(
} map toApiResult
}

private val GameRateLimitPerIP = lila.memo.RateLimit[IpAddress](
credits = 100,
duration = 1.minute,
key = "game.api.one.ip"
)

def game(id: GameId) = ApiRequest:
GameRateLimitPerIP(req.ipAddress, fuccess(ApiResult.Limited), cost = 1):
lila.mon.api.game.increment(1)
gameApi.one(id, gameFlagsFromRequest(req)) map toApiResult
gameApi.one(id, gameFlagsFromRequest(req)) map toApiResult

private val CrosstableRateLimitPerIP = lila.memo.RateLimit[IpAddress](
credits = 30,
duration = 10.minutes,
key = "crosstable.api.ip"
)

def crosstable(name1: UserStr, name2: UserStr) =
ApiRequest:
CrosstableRateLimitPerIP(req.ipAddress, fuccess(ApiResult.Limited), cost = 1):
val (u1, u2) = (name1.id, name2.id)
env.game.crosstableApi(u1, u2) flatMap { ct =>
(ct.results.nonEmpty && getBool("matchup", req)).?? {
env.game.crosstableApi.getMatchup(u1, u2)
} map { matchup =>
toApiResult:
lila.game.JsonView.crosstable(ct, matchup).some
}
def crosstable(name1: UserStr, name2: UserStr) = ApiRequest:
CrosstableRateLimitPerIP(req.ipAddress, fuccess(ApiResult.Limited), cost = 1):
val (u1, u2) = (name1.id, name2.id)
env.game.crosstableApi(u1, u2) flatMap { ct =>
(ct.results.nonEmpty && getBool("matchup", req)).?? {
env.game.crosstableApi.getMatchup(u1, u2)
} map { matchup =>
toApiResult:
lila.game.JsonView.crosstable(ct, matchup).some
}
}

def currentTournaments = ApiRequest:
given Lang = reqLang
Expand Down
44 changes: 24 additions & 20 deletions app/controllers/Auth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import views.*

import lila.api.Context
import lila.app.{ given, * }
import lila.common.{ EmailAddress, HTTPRequest }
import lila.common.{ EmailAddress, HTTPRequest, IpAddress }
import lila.memo.RateLimit
import lila.security.SecurityForm.{ MagicLink, PasswordReset }
import lila.security.{ FingerPrint, Signup }
Expand Down Expand Up @@ -67,7 +67,7 @@ final class Auth(

private def authenticateCookie(sessionId: String, remember: Boolean)(
result: Result
)(using req: RequestHeader) =
)(using RequestHeader) =
result.withCookies(
env.lilaCookie.withSession(remember = remember) {
_ + (api.sessionIdKey -> sessionId) - api.AccessUri - lila.security.EmailConfirm.cookie.name
Expand Down Expand Up @@ -136,7 +136,7 @@ final class Auth(
)
},
result =>
result.toOption match {
result.toOption match
case None => InternalServerError("Authentication error").toFuccess
case Some(u) if u.enabled.no =>
negotiate(
Expand All @@ -151,7 +151,6 @@ final class Auth(
env.user.repo.email(u.id) foreach { _ foreach garbageCollect(u) }
val remember = api.rememberForm.bindFromRequest().value | true
authenticateUser(u, remember, Some(redirectTo))
}
)
}
}
Expand Down Expand Up @@ -182,8 +181,8 @@ final class Auth(
forms.signup.website.map: form =>
Ok(html.auth.signup(form))

private def authLog(user: String, email: String, msg: String) =
lila.log("auth").info(s"$user $email $msg")
private def authLog(user: UserName, email: Option[EmailAddress], msg: String) =
lila.log("auth").info(s"$user ${email.fold("-")(_.value)} $msg")

def signupPost = OpenBody:
NoTor:
Expand Down Expand Up @@ -288,7 +287,7 @@ final class Auth(
lila.mon.user.register.confirmEmailResult(true).increment()
env.user.repo.email(user.id).flatMap {
_.?? { email =>
authLog(user.username, email.value, s"Confirmed email ${email.value}")
authLog(user.username, email.some, s"Confirmed email ${email.value}")
welcome(user, email, sendWelcomeEmail = false)
}
} >> redirectNewUser(user)
Expand Down Expand Up @@ -364,7 +363,7 @@ final class Auth(
lila.mon.user.auth.passwordResetConfirm("tokenFail").increment()
notFound
case Some(user) =>
authLog(user.username, "-", "Reset password")
authLog(user.username, none, "Reset password")
lila.mon.user.auth.passwordResetConfirm("tokenOk").increment()
fuccess(html.auth.bits.passwordResetConfirm(user, token, forms.passwdResetFor(user), none))
}
Expand Down Expand Up @@ -451,7 +450,7 @@ final class Auth(
lila.mon.user.auth.magicLinkConfirm("token_fail").increment()
notFound
case Some(user) =>
authLog(user.username, "-", "Magic link")
authLog(user.username, none, "Magic link")
authenticateUser(user, remember = true) >>-
lila.mon.user.auth.magicLinkConfirm("success").increment().unit
}
Expand Down Expand Up @@ -502,17 +501,22 @@ final class Auth(

private given limitedDefault: Zero[Result] = Zero(rateLimited)

private[controllers] def LoginRateLimit(id: UserIdOrEmail, req: RequestHeader)(
run: RateLimit.Charge => Fu[Result]
): Fu[Result] =
env.security.ipTrust
.isSuspicious(req.ipAddress)
.flatMap: ipSusp =>
PasswordHasher.rateLimit[Result](
rateLimitedFu,
enforce = env.net.rateLimit,
ipCost = 1 + ipSusp.??(15) + EmailAddress.isValid(id.value).??(2)
)(id, req)(run)
private[controllers] object LoginRateLimit:
private val lastAttemptIp =
env.memo.cacheApi.notLoadingSync[UserIdOrEmail, IpAddress](64, "login.lastIp"):
_.expireAfterWrite(10.seconds).build()
def apply(id: UserIdOrEmail, req: RequestHeader)(run: RateLimit.Charge => Fu[Result]): Fu[Result] =
val ip = req.ipAddress
val multipleIps = lastAttemptIp.asMap().put(id, ip).fold(false)(_ != ip)
env.security.ipTrust
.isSuspicious(ip)
.flatMap: ipSusp =>
PasswordHasher.rateLimit[Result](
rateLimitedFu,
enforce = env.net.rateLimit,
ipCost = 1 + ipSusp.??(15) + EmailAddress.isValid(id.value).??(2),
userCost = 1 + multipleIps.??(4)
)(id, req)(run)

private[controllers] def HasherRateLimit(id: UserId, req: RequestHeader)(run: => Fu[Result]): Fu[Result] =
env.security
Expand Down
46 changes: 22 additions & 24 deletions app/controllers/Challenge.scala
Original file line number Diff line number Diff line change
Expand Up @@ -258,30 +258,28 @@ final class Challenge(
)

def toFriend(id: ChallengeId) = AuthBody { ctx ?=> _ =>
NoBot:
import play.api.data.*
import play.api.data.Forms.*
OptionFuResult(api byId id) { c =>
if (isMine(c))
Form(single("username" -> lila.user.UserForm.historicalUsernameField))
.bindFromRequest()
.fold(
_ => funit,
username =>
ChallengeIpRateLimit(ctx.ip, rateLimitedFu):
env.user.repo byId username flatMap {
case None => Redirect(routes.Challenge.show(c.id)).toFuccess
case Some(dest) if ctx.is(dest) => Redirect(routes.Challenge.show(c.id)).toFuccess
case Some(dest) =>
env.challenge.granter.isDenied(ctx.me, dest, c.perfType.some) flatMap {
case Some(denied) =>
showChallenge(c, lila.challenge.ChallengeDenied.translated(denied).some)
case None => api.setDestUser(c, dest) inject Redirect(routes.Challenge.show(c.id))
}
}
)
else notFound
}
import play.api.data.*
import play.api.data.Forms.*
OptionFuResult(api byId id): c =>
if isMine(c) then
Form(single("username" -> lila.user.UserForm.historicalUsernameField))
.bindFromRequest()
.fold(
_ => funit,
username =>
ChallengeIpRateLimit(ctx.ip, rateLimitedFu):
env.user.repo byId username flatMap {
case None => Redirect(routes.Challenge.show(c.id)).toFuccess
case Some(dest) if ctx.is(dest) => Redirect(routes.Challenge.show(c.id)).toFuccess
case Some(dest) =>
env.challenge.granter.isDenied(ctx.me, dest, c.perfType.some) flatMap {
case Some(denied) =>
showChallenge(c, lila.challenge.ChallengeDenied.translated(denied).some)
case None => api.setDestUser(c, dest) inject Redirect(routes.Challenge.show(c.id))
}
}
)
else notFound
}

def apiCreate(username: UserStr) = ScopedBody(_.Challenge.Write, _.Bot.Play, _.Board.Play) { req ?=> me =>
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/CtrlExtensions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ trait CtrlExtensions extends ControllerHelpers:
def enableSharedArrayBuffer(using req: RequestHeader): Result = {
if HTTPRequest.isChrome96Plus(req) then
result.withHeaders("Cross-Origin-Embedder-Policy" -> "credentialless")
else if HTTPRequest.isFirefox112Plus(req) && env.firefoxOriginTrial.get().nonEmpty then
else if HTTPRequest.isFirefox114Plus(req) && env.firefoxOriginTrial.get().nonEmpty then
result.withHeaders(
"Origin-Trial" -> env.firefoxOriginTrial.get(),
"Cross-Origin-Embedder-Policy" -> "credentialless"
Expand Down
3 changes: 1 addition & 2 deletions app/controllers/Dev.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,11 @@ final class Dev(env: Env) extends LilaController(env):
.bindFromRequest()
.fold(
_ => BadRequest(html.dev.settings(settingsList)).toFuccess,
v => {
v =>
lila
.log("setting")
.info(s"${me.user.username} changes $id from ${setting.get()} to ${v.toString}")
setting.setString(v.toString) inject Redirect(routes.Dev.settings)
}
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/Fishnet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ final class Fishnet(env: Env) extends LilaController(env):
},
{
case PostAnalysisResult.Complete(analysis) =>
env.round.proxyRepo.updateIfPresent(GameId(analysis.id))(_.setAnalysed)
env.round.proxyRepo.updateIfPresent(analysis.id into GameId)(_.setAnalysed)
onComplete
case _: PostAnalysisResult.Partial => fuccess(Left(NoContent))
case PostAnalysisResult.UnusedPartial => fuccess(Left(NoContent))
Expand Down
64 changes: 22 additions & 42 deletions app/controllers/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -83,49 +83,13 @@ final class Main(
fuccess:
Ok:
if env.net.crawlable && req.domain == env.net.domain.value && env.net.isProd
then """User-agent: *
Allow: /
Disallow: /game/export/
Disallow: /games/export/
Disallow: /api/
Disallow: /opening/config/
Allow: /game/export/gif/thumbnail/
User-agent: Twitterbot
Allow: /
"""
then lila.api.StaticContent.robotsTxt
else "User-agent: *\nDisallow: /"

def manifest = Anon:
import lila.common.Json.given
JsonOk:
Json.obj(
"name" -> env.net.domain,
"short_name" -> "Lichess",
"start_url" -> "/",
"display" -> "standalone",
"background_color" -> "#161512",
"theme_color" -> "#161512",
"description" -> "The (really) free, no-ads, open source chess server.",
"icons" -> List(32, 64, 128, 192, 256, 512, 1024).map { size =>
Json.obj(
"src" -> s"//${env.net.assetDomain}/assets/logo/lichess-favicon-$size.png",
"sizes" -> s"${size}x$size",
"type" -> "image/png"
)
},
"related_applications" -> Json.arr(
Json.obj(
"platform" -> "play",
"url" -> "https://play.google.com/store/apps/details?id=org.lichess.mobileapp"
),
Json.obj(
"platform" -> "itunes",
"url" -> "https://itunes.apple.com/us/app/lichess-free-online-chess/id968371784"
)
)
)
.toFuccess
fuccess:
JsonOk:
lila.api.StaticContent.manifest(env.net)

def getFishnet = Open:
pageHit
Expand Down Expand Up @@ -177,7 +141,7 @@ Allow: /
)

def legacyQaQuestion(id: Int, slug: String) = Open:
MovedPermanently {
MovedPermanently:
val faq = routes.Main.faq.url
id match
case 103 => s"$faq#acpl"
Expand All @@ -198,6 +162,22 @@ Allow: /
case 46 => s"$faq#name"
case 122 => s"$faq#marks"
case _ => faq
}.toFuccess
.toFuccess

def devAsset(v: String, path: String, file: String) = assetsC.at(path, file)

private val ImageUploadRateLimitPerIp = lila.memo.RateLimit.composite[lila.common.IpAddress](
key = "image.upload.ip"
)(
("fast", 10, 2.minutes),
("slow", 60, 1.day)
)

def uploadImage(rel: String) = AuthBody(parse.multipartFormData) { ctx ?=> me =>
ctx.body.body.file("image") match
case Some(image) =>
env.memo.picfitApi.bodyImage
.upload(rel = rel, image = image, me = me.id, ip = ctx.ip)
.map(url => JsonOk(Json.obj("imageUrl" -> url)))
case None => fuccess(JsonBadRequest(jsonError("Image content only")))
}

0 comments on commit 86a73f3

Please sign in to comment.