Skip to content

Commit

Permalink
Merge branch 'master' into challenges-a11y
Browse files Browse the repository at this point in the history
* master: (414 commits)
  setup rating: final checks and simplification
  fix more duplicated code
  fix duplicated code, fix bug when ratingMap[perf] doesn't exist
  remove superfluous check, the type says it always returns a Perf
  Revert "fewer dom text nodes and more css tricks"
  only feature swiss tournaments where players actually play
  scala tweaks
  scala tweaks
  New Crowdin updates (lichess-org#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
  ...
  • Loading branch information
ornicar committed Jun 5, 2023
2 parents cc651e8 + 0278cd5 commit 811c1e7
Show file tree
Hide file tree
Showing 1,165 changed files with 24,044 additions and 6,577 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
4 changes: 3 additions & 1 deletion .github/workflows/assets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ on:
- 'ui/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'bin/download-lifat'
pull_request:
paths:
- '.github/workflows/assets.yml'
- 'public/**'
- 'ui/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'bin/download-lifat'

jobs:
assets:
Expand All @@ -25,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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ public/vendor/stockfish.wasm
public/vendor/stockfish-nnue.wasm
public/vendor/stockfish-mv.wasm
public/vendor/stockfish.js
public/lifat
public/css/
target
data/
dist/
node_modules/
local/
lifat/
ui/common/**/*.js
ui/common/**/*.d.ts
ui/chess/**/*.js
Expand All @@ -35,6 +37,7 @@ ui/nvui/**/*.js
ui/nvui/**/*.d.ts
ui/puz/**/*.js
ui/puz/**/*.d.ts
ui/voice/@build/crowdv
ui/*/npm-debug.log
ui/*/tsconfig.tsbuildinfo
hs_*.log
Expand All @@ -54,5 +57,6 @@ RUNNING_PID
# IntelliJ auto-generated files
.idea

# Docker-compose
# Docker-compose / docker sbt
/docker-compose.yml
/?
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
/ui/common/**/*.d.ts
/ui/game/**/*.js
/ui/game/**/*.d.ts
/ui/voice/@build/**/*.json
/ui/nvui/**/*.js
/ui/nvui/**/*.d.ts
/ui/puz/**/*.js
Expand Down
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
2 changes: 0 additions & 2 deletions app/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ final class Env(
lazy val gamePaginator = wire[mashup.GameFilterMenu.PaginatorBuilder]
lazy val pageCache = wire[http.PageCache]

@annotation.nowarn("msg=unused")
private val tryDailyPuzzle: lila.puzzle.DailyPuzzle.Try = () =>
Future {
puzzle.daily.get
Expand All @@ -159,7 +158,6 @@ final class Env(
system.actorOf(Props(new templating.RendererActor), name = config.get[String]("hub.actor.renderer"))
end Env

@annotation.nowarn("msg=unused")
final class EnvBoot(
config: Configuration,
environment: Environment,
Expand Down
2 changes: 1 addition & 1 deletion app/LilaComponents.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ final class LilaComponents(

given ActorSystem = actorSystem

implicit lazy val httpClient: StandaloneWSClient =
given StandaloneWSClient =
import play.shaded.ahc.org.asynchttpclient.DefaultAsyncHttpClient
import play.api.libs.ws.WSConfigParser
import play.api.libs.ws.ahc.{ AhcConfigBuilder, AhcWSClientConfigParser, StandaloneAhcWSClient }
Expand Down
32 changes: 12 additions & 20 deletions app/controllers/Account.scala
Original file line number Diff line number Diff line change
Expand Up @@ -93,18 +93,15 @@ final class Account(
val apiMe =
val rateLimit = lila.memo.RateLimit[UserId](30, 10.minutes, "api.account.user")
Scoped() { req ?=> me =>
rateLimit(me.id) {
def limited = rateLimitedFu:
"Please don't poll this endpoint. Stream https://lichess.org/api#tag/Board/operation/apiStreamEvent instead."
rateLimit(me.id, limited):
env.api.userApi.extended(
me,
me.some,
withFollows = apiC.userWithFollows(req),
withTrophies = false
)(using I18nLangPicker(req, me.lang)) dmap { JsonOk(_) }
}(
rateLimitedFu(
"Please don't poll this endpoint. Stream https://lichess.org/api#tag/Board/operation/apiStreamEvent instead."
)
)
)(using reqLang(me)) dmap { JsonOk(_) }
}

def apiNowPlaying = Scoped() { req ?=> me =>
Expand All @@ -121,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 Expand Up @@ -179,7 +175,7 @@ final class Account(
}
}

def renderCheckYourEmail(implicit ctx: Context) =
def renderCheckYourEmail(using Context) =
html.auth.checkYourEmail(lila.security.EmailConfirm.cookie get ctx.req)

def emailApply = AuthBody { ctx ?=> me =>
Expand All @@ -189,12 +185,10 @@ final class Account(
fuccess(html.account.email(err))
} { data =>
val newUserEmail = lila.security.EmailConfirm.UserEmail(me.username, data.email)
auth.EmailConfirmRateLimit(newUserEmail, ctx.req) {
auth.EmailConfirmRateLimit(newUserEmail, ctx.req, rateLimitedFu):
env.security.emailChange.send(me, newUserEmail.email) inject
Redirect(routes.Account.email).flashSuccess {
Redirect(routes.Account.email).flashSuccess:
lila.i18n.I18nKeys.checkYourEmail.txt()
}
}(rateLimitedFu)
}
}
}
Expand Down Expand Up @@ -327,7 +321,7 @@ final class Account(
case Some(v) => env.user.repo.setKid(me, v) inject jsonOkResult
}

private def currentSessionId(implicit ctx: Context) =
private def currentSessionId(using Context) =
~env.security.api.reqSessionId(ctx.req)

def security = Auth { _ ?=> me =>
Expand Down Expand Up @@ -382,12 +376,10 @@ final class Account(
lila.mon.user.auth.reopenRequest(code).increment()
renderReopen(none, msg.some) map { BadRequest(_) }
case Right(user) =>
auth.MagicLinkRateLimit(user, data.email, ctx.req) {
auth.MagicLinkRateLimit(user, data.email, ctx.req, rateLimitedFu):
lila.mon.user.auth.reopenRequest("success").increment()
env.security.reopen.send(user, data.email) inject Redirect(
env.security.reopen.send(user, data.email) inject Redirect:
routes.Account.reopenSent
)
}(rateLimitedFu)
}
)
}
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
69 changes: 25 additions & 44 deletions app/controllers/Api.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import play.api.libs.json.*
import play.api.mvc.*
import scala.util.chaining.*

import lila.api.{ Context, GameApiV2 }
import lila.api.GameApiV2
import lila.app.{ given, * }
import lila.common.config.{ MaxPerPage, MaxPerSecond }
import lila.common.{ HTTPRequest, IpAddress, LightUser }
Expand Down Expand Up @@ -37,14 +37,13 @@ final class Api(
private val userRateLimit = env.security.ipTrust.rateLimit(3_000, 1.day, "user.show.api.ip")
def user(name: UserStr) =
def get(req: RequestHeader, me: Option[lila.user.User], lang: Lang) =
userRateLimit(req.ipAddress) {
userRateLimit(req.ipAddress, rateLimitedFu):
userApi.extended(
name,
me,
withFollows = userWithFollows(req),
withTrophies = getBool("trophies", req)
)(using lang) map toApiResult map toHttp
}(rateLimitedFu)
OpenOrScoped()(
ctx ?=> get(ctx.req, ctx.me, ctx.lang),
req ?=> me => get(req, me.some, me.realLang | reqLang(using req))
Expand All @@ -64,12 +63,11 @@ final class Api(
def usersByIds = AnonBodyOf(parse.tolerantText): body =>
val usernames = body.replace("\n", "").split(',').take(300).flatMap(UserStr.read).toList
val cost = usernames.size / 4
UsersRateLimitPerIP(req.ipAddress, cost = cost) {
UsersRateLimitPerIP(req.ipAddress, rateLimitedFu, cost = cost):
lila.mon.api.users.increment(cost.toLong)
env.user.repo byIds usernames map {
_.map { env.user.jsonView.full(_, none, withRating = true, withProfile = true) }
} map toApiResult map toHttp
}(rateLimitedFu)

def usersStatus = ApiRequest:
val ids = get("ids", req).??(_.split(',').take(100).toList flatMap UserStr.read).map(_.id)
Expand Down Expand Up @@ -110,14 +108,12 @@ final class Api(
)

private def UserGamesRateLimit(cost: Int, req: RequestHeader)(run: => Fu[ApiResult]) =
val ip = req.ipAddress
UserGamesRateLimitPerIP(ip, cost = cost) {
UserGamesRateLimitPerUA(HTTPRequest.userAgent(req), cost = cost, msg = ip.value) {
UserGamesRateLimitGlobal("-", cost = cost, msg = ip.value) {
val ip = req.ipAddress
def limited = fuccess(ApiResult.Limited)
UserGamesRateLimitPerIP(ip, limited, cost = cost):
UserGamesRateLimitPerUA(HTTPRequest.userAgent(req), limited, cost = cost, msg = ip.value):
UserGamesRateLimitGlobal("-", limited, cost = cost, msg = ip.value):
run
}(fuccess(ApiResult.Limited))
}(fuccess(ApiResult.Limited))
}(fuccess(ApiResult.Limited))

private def gameFlagsFromRequest(req: RequestHeader) =
lila.api.GameApi.WithFlags(
Expand Down Expand Up @@ -149,37 +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, cost = 1) {
lila.mon.api.game.increment(1)
gameApi.one(id, gameFlagsFromRequest(req)) map toApiResult
}(fuccess(ApiResult.Limited))
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, 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
}
}(fuccess(ApiResult.Limited))
}

def currentTournaments = ApiRequest:
given Lang = reqLang
Expand Down Expand Up @@ -304,7 +289,7 @@ final class Api(
}

def gamesByUsersStream = AnonOrScopedBody(parse.tolerantText)() { req ?=> me =>
val max = me.fold(300) { u => if u == lila.user.User.lichess4545Id then 900 else 500 }
val max = me.fold(300) { u => if u is lila.user.User.lichess4545Id then 900 else 500 }
withIdsFromReqBody[UserId](req, max, id => UserStr.read(id).map(_.id)) { ids =>
GlobalConcurrencyLimitPerIP.events(req.ipAddress)(
addKeepAlive:
Expand Down Expand Up @@ -349,7 +334,7 @@ final class Api(
val cloudEval =
val rateLimit = lila.memo.RateLimit[IpAddress](3_000, 1.day, "cloud-eval.api.ip")
Anon:
rateLimit(req.ipAddress) {
rateLimit(req.ipAddress, rateLimitedFu):
get("fen", req).fold(notFoundJson("Missing FEN")): fen =>
import chess.variant.Variant
JsonOptionOk:
Expand All @@ -358,21 +343,18 @@ final class Api(
chess.format.Fen.Epd.clean(fen),
getIntAs[MultiPv]("multiPv", req) | MultiPv(1)
)
}(rateLimitedFu)

val eventStream =
val rateLimit = lila.memo.RateLimit[UserId](30, 10.minutes, "api.stream.event.user")
Scoped(_.Bot.Play, _.Board.Play, _.Challenge.Read) { _ ?=> me =>
rateLimit(me.id) {
def limited = rateLimitedFu:
"Please don't poll this endpoint, it is intended to be streamed. See https://lichess.org/api#tag/Board/operation/apiStreamEvent."
rateLimit(me.id, limited):
env.round.proxyRepo.urgentGames(me) flatMap { povs =>
env.challenge.api.createdByDestId(me.id) map { challenges =>
sourceToNdJsonOption(env.api.eventStream(me, povs.map(_.game), challenges))
}
}
}(
rateLimitedFu:
"Please don't poll this endpoint, it is intended to be streamed. See https://lichess.org/api#tag/Board/operation/apiStreamEvent."
)
}

private val UserActivityRateLimitPerIP = lila.memo.RateLimit[IpAddress](
Expand All @@ -383,14 +365,13 @@ final class Api(

def activity(name: UserStr) = ApiRequest:
given Lang = reqLang
UserActivityRateLimitPerIP(req.ipAddress, cost = 1) {
UserActivityRateLimitPerIP(req.ipAddress, fuccess(ApiResult.Limited), cost = 1):
lila.mon.api.activity.increment(1)
env.user.repo byId name flatMapz { user =>
env.activity.read.recentAndPreload(user) flatMap {
_.map { env.activity.jsonView(_, user) }.parallel
}
} map toApiResult
}(fuccess(ApiResult.Limited))

private val ApiMoveStreamGlobalConcurrencyLimitPerIP =
lila.memo.ConcurrencyLimit[IpAddress](
Expand Down

0 comments on commit 811c1e7

Please sign in to comment.