Skip to content

Commit

Permalink
Merge branch 'master' into piece-adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
johndoknjas authored Jan 20, 2025
2 parents 1856001 + 35cbd7d commit 8de55a8
Show file tree
Hide file tree
Showing 113 changed files with 522 additions and 513 deletions.
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "3.8.4"
version = "3.8.5"
runner.dialect = scala3

align.preset = more
Expand Down
36 changes: 20 additions & 16 deletions app/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,36 @@ final class Env(
val config: Configuration,
val controllerComponents: ControllerComponents,
environment: Environment,
shutdown: akka.actor.CoordinatedShutdown
shutdown: akka.actor.CoordinatedShutdown,
cookieBaker: SessionCookieBaker
)(using val system: akka.actor.ActorSystem, val executor: Executor)(using
StandaloneWSClient,
akka.stream.Materializer,
SessionCookieBaker
akka.stream.Materializer
):
val net: NetConfig = config.get[NetConfig]("net")

export net.{ domain, baseUrl, assetBaseUrlInternal }
export net.{ baseUrl, assetBaseUrlInternal }

given mode: Mode = environment.mode
given translator: lila.core.i18n.Translator = lila.i18n.Translator
given scheduler: Scheduler = system.scheduler
given lila.core.config.RateLimit = net.rateLimit
given RateLimit = net.rateLimit
given NetDomain = net.domain

// wire all the lila modules in the right order
val i18n: lila.i18n.Env.type = lila.i18n.Env
val mongo: lila.db.Env = wire[lila.db.Env]
val memo: lila.memo.Env = wire[lila.memo.Env]
val socket: lila.socket.Env = wire[lila.socket.Env]
val user: lila.user.Env = wire[lila.user.Env]
val mailer: lila.mailer.Env = wire[lila.mailer.Env]
val oAuth: lila.oauth.Env = wire[lila.oauth.Env]
val security: lila.security.Env = wire[lila.security.Env]
val pref: lila.pref.Env = wire[lila.pref.Env]
val relation: lila.relation.Env = wire[lila.relation.Env]
val game: lila.game.Env = wire[lila.game.Env]
val i18n: lila.i18n.Env.type = lila.i18n.Env
val mongo: lila.db.Env = wire[lila.db.Env]
val memo: lila.memo.Env = wire[lila.memo.Env]
val socket: lila.socket.Env = wire[lila.socket.Env]
val user: lila.user.Env = wire[lila.user.Env]
import user.flairApi.given
val mailer: lila.mailer.Env = wire[lila.mailer.Env]
val oAuth: lila.oauth.Env = wire[lila.oauth.Env]
val security: lila.security.Env = wire[lila.security.Env]
val pref: lila.pref.Env = wire[lila.pref.Env]
val relation: lila.relation.Env = wire[lila.relation.Env]
val game: lila.game.Env = wire[lila.game.Env]
import game.given
val notifyM: lila.notify.Env = wire[lila.notify.Env]
val irc: lila.irc.Env = wire[lila.irc.Env]
val report: lila.report.Env = wire[lila.report.Env]
Expand All @@ -62,6 +65,7 @@ final class Env(
val forum: lila.forum.Env = wire[lila.forum.Env]
val forumSearch: lila.forumSearch.Env = wire[lila.forumSearch.Env]
val pool: lila.pool.Env = wire[lila.pool.Env]
import pool.isClockCompatible
val lobby: lila.lobby.Env = wire[lila.lobby.Env]
val setup: lila.setup.Env = wire[lila.setup.Env]
val simul: lila.simul.Env = wire[lila.simul.Env]
Expand Down
6 changes: 6 additions & 0 deletions app/controllers/Api.scala
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,12 @@ final class Api(
ttl = 1.hour,
maxConcurrency = 4
)
val eventsForVerifiedUser = lila.web.ConcurrencyLimit[IpAddress](
name = "API verified events concurrency per IP",
key = "api.ip.events.verified",
ttl = 1.hour,
maxConcurrency = 12
)
val download = lila.web.ConcurrencyLimit[IpAddress](
name = "API download concurrency per IP",
key = "api.ip.download",
Expand Down
9 changes: 6 additions & 3 deletions app/controllers/RelayRound.scala
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,13 @@ final class RelayRound(

def stream(id: RelayRoundId) = AnonOrScoped(): ctx ?=>
Found(env.relay.api.byIdWithStudy(id)): rs =>
val limiter =
if ctx.me.exists(_.isVerified)
then apiC.GlobalConcurrencyLimitPerIP.eventsForVerifiedUser
else apiC.GlobalConcurrencyLimitPerIP.events
studyC.CanView(rs.study) {
apiC.GlobalConcurrencyLimitPerIP
.events(req.ipAddress)(env.relay.pgnStream.streamRoundGames(rs)): source =>
noProxyBuffer(Ok.chunked[PgnStr](source.keepAlive(60.seconds, () => PgnStr(" "))))
limiter(req.ipAddress)(env.relay.pgnStream.streamRoundGames(rs)): source =>
noProxyBuffer(Ok.chunked[PgnStr](source.keepAlive(60.seconds, () => PgnStr(" "))))
}(Unauthorized, Forbidden)

def chapter(ts: String, rs: String, id: RelayRoundId, chapterId: StudyChapterId) =
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ final class Setup(
doubleJsonFormError,
config =>
processor.apiAi(config).map { pov =>
Created(env.game.jsonView.baseWithChessDenorm(pov.game, config.fen)).as(JSON)
val json = env.game.jsonView.apiAiNewGame(pov, config.fen)
Created(json).as(JSON)
}
)
}
Expand Down
6 changes: 4 additions & 2 deletions app/views/base/embed.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ object embed:
st.headTitle(title),
(ctx.bg == "system").option(page.ui.systemThemeScript(ctx.nonce.some)),
page.ui.pieceSprite(ctx.pieceSet.name),
cssTag("common.theme.embed"), // includes both light & dark colors
cssTag("common.theme.embed"),
link(rel := "stylesheet", href := assetUrl("css/theme/font-face.css")),
cssKeys.map(cssTag),
page.ui.scriptsPreload(modules.flatMap(_.map(_.key)))
),
Expand Down Expand Up @@ -65,7 +66,8 @@ object embed:
st.headTitle(title),
(ctx.bg == "system").option(page.ui.systemThemeScript(ctx.nonce.some)),
page.ui.pieceSprite(ctx.pieceSet.name),
cssTag("common.theme.embed"), // includes both light & dark colors
cssTag("common.theme.embed"),
link(rel := "stylesheet", href := assetUrl("css/theme/font-face.css")),
cssKeys.map(cssTag),
page.ui.sitePreload(
List[I18nModule.Selector](_.site, _.timeago) ++ i18nModules,
Expand Down
1 change: 1 addition & 0 deletions app/views/base/page.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ object page:
else s"${ctx.me.so(_.username.value + " ")} $prodTitle"
,
cssTag("common.theme.all"),
link(rel := "stylesheet", href := assetUrl("css/theme/font-face.css")),
cssTag("site"),
pref.is3d.option(cssTag("common.board-3d")),
ctx.data.inquiry.isDefined.option(cssTag("mod.inquiry")),
Expand Down
2 changes: 1 addition & 1 deletion app/views/mod/inquiry.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object inquiry:
)

def apply(in: lila.mod.Inquiry)(using ctx: Context) =
div(id := "inquiry")(
div(id := "inquiry", data("username") := in.user.user.username)(
i(title := "Costello the Inquiry Octopus", cls := "costello"),
div(cls := "meat")(
userLink(in.user.user, withPerfRating = in.user.perfs.some, params = "?mod"),
Expand Down
4 changes: 2 additions & 2 deletions app/views/user/show/header.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ object header:
a(
href := routes.Plan.index(),
cls := "trophy award patron icon3d",
ariaTitle(s"Patron since ${showDate(u.plan.sinceDate)}")
ariaTitle(trans.patron.patronSince.txt(showDate(u.plan.sinceDate)))
)(patronIconChar)
)
),
Expand Down Expand Up @@ -82,7 +82,7 @@ object header:
cls := "nm-item",
href := routes.Ublog.index(u.username)
)(
splitNumber(s"${info.ublog.so(_.nbPosts)} blog posts")
splitNumber(trans.ublog.blogPosts.pluralSame(info.ublog.so(_.nbPosts)))
)
),
(ctx.isAuth && ctx.isnt(u))
Expand Down
3 changes: 1 addition & 2 deletions bin/gen/licon.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def main():

gen_sources(codes)

print('Generated:\n public/font/lichess.woff\n public/font/lichess.woff2\n public/font/lichess.ttf')
print('Generated:\n public/font/lichess.woff2\n public/font/lichess.ttf\n public/oops/font.html')
print(' modules/ui/src/main/Icon.scala\n ui/common/src/licon.ts')
print(' ui/common/css/abstract/_licon.scss\n')
print("Don't forget to install lichess.ttf in your code editor\n")
Expand Down Expand Up @@ -158,7 +158,6 @@ def gen_fonts():
[f, name] = tempfile.mkstemp(suffix='.pe', dir='.')
os.write(f, textwrap.dedent(f"""
Open('lichess.sfd')
Generate('lichess.woff')
Generate('lichess.woff2')
Generate('lichess.ttf')
Quit()
Expand Down
2 changes: 2 additions & 0 deletions modules/common/src/main/mon.scala
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,8 @@ object mon:
counter("security.githubSecretScanning.hit").withTags(
tags("type" -> tokenType, "source" -> source, "hit" -> hit)
)
def userTrust(trust: Boolean, cause: String) =
counter("security.userTrust").withTags(tags("trust" -> trust, "cause" -> cause)).increment()
object shutup:
def analyzer = timer("shutup.analyzer.time").withoutTags()
object tv:
Expand Down
2 changes: 2 additions & 0 deletions modules/coreI18n/src/main/key.scala
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,7 @@ object I18nKey:
val `donate`: I18nKey = "patron:donate"
val `donateAsX`: I18nKey = "patron:donateAsX"
val `lichessPatron`: I18nKey = "patron:lichessPatron"
val `patronSince`: I18nKey = "patron:patronSince"
val `freeAccount`: I18nKey = "patron:freeAccount"
val `becomePatron`: I18nKey = "patron:becomePatron"
val `xBecamePatron`: I18nKey = "patron:xBecamePatron"
Expand Down Expand Up @@ -2783,6 +2784,7 @@ object I18nKey:
val `blogTips`: I18nKey = "ublog:blogTips"
val `discussThisBlogPostInTheForum`: I18nKey = "ublog:discussThisBlogPostInTheForum"
val `youBlockedByBlogAuthor`: I18nKey = "ublog:youBlockedByBlogAuthor"
val `blogPosts`: I18nKey = "ublog:blogPosts"
val `publishedNbBlogPosts`: I18nKey = "ublog:publishedNbBlogPosts"
val `nbViews`: I18nKey = "ublog:nbViews"
val `viewAllNbPosts`: I18nKey = "ublog:viewAllNbPosts"
Expand Down
4 changes: 2 additions & 2 deletions modules/game/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ final class Env(

val gameRepo = GameRepo(db(config.gameColl))

val idGenerator = wire[IdGenerator]
given idGenerator: IdGenerator = wire[IdGenerator]

val divider = wire[Divider]

Expand Down Expand Up @@ -82,7 +82,7 @@ final class Env(
export AnonCookie.json as anonCookieJson
export AnonCookie.name as anonCookieName

lazy val newPlayer: lila.core.game.NewPlayer = new:
given newPlayer: lila.core.game.NewPlayer = new:
export Player.make as apply
export Player.makeAnon as anon

Expand Down
3 changes: 3 additions & 0 deletions modules/game/src/main/JsonView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ final class JsonView(rematches: Rematches):
.add("check" -> game.situation.checkSquare.map(_.key))
.add("lastMove" -> game.lastMoveKeys)

def apiAiNewGame(pov: Pov, initialFen: Option[Fen.Full]): JsObject =
baseWithChessDenorm(pov.game, initialFen) ++ Json.obj("fullId" -> pov.fullId)

def ownerPreview(pov: Pov)(using LightUser.GetterSync) =
Json
.obj(
Expand Down
4 changes: 2 additions & 2 deletions modules/irc/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import lila.core.misc.plan.ChargeEvent
@Module
final class Env(
appConfig: Configuration,
getLightUser: lila.core.LightUser.Getter,
noteApi: lila.core.user.NoteApi,
ws: StandaloneWSClient,
shutdown: akka.actor.CoordinatedShutdown,
mode: Mode
mode: Mode,
getLightUser: lila.core.LightUser.Getter
)(using Executor):

import ZulipClient.given
Expand Down
5 changes: 3 additions & 2 deletions modules/irc/src/main/IrcApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import lila.core.study.data.StudyChapterName

final class IrcApi(
zulip: ZulipClient,
noteApi: lila.core.user.NoteApi
)(using lightUser: LightUser.Getter, ec: Executor)
noteApi: lila.core.user.NoteApi,
lightUser: LightUser.Getter
)(using Executor)
extends lila.core.irc.IrcApi:

import IrcApi.*
Expand Down
2 changes: 1 addition & 1 deletion modules/pool/src/main/PoolList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ object PoolList:

val clockStringSet: Set[String] = all.view.map(_.clock.show) to Set

val isClockCompatible: IsClockCompatible = IsClockCompatible: clock =>
given isClockCompatible: IsClockCompatible = IsClockCompatible: clock =>
clockStringSet contains clock.show

def json(using lila.core.i18n.Translator) = Json.toJson(all)
4 changes: 2 additions & 2 deletions modules/push/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ final class Env(
appConfig: Configuration,
ws: StandaloneWSClient,
db: lila.db.Db,
getLightUser: lila.core.LightUser.GetterFallback,
gameProxy: lila.core.game.GameProxy,
roundJson: lila.core.round.RoundJson,
gameRepo: lila.core.game.GameRepo,
namer: lila.core.game.Namer,
notifyAllows: lila.core.notify.GetNotifyAllows,
postApi: lila.core.forum.ForumPostApi
postApi: lila.core.forum.ForumPostApi,
getLightUser: lila.core.LightUser.GetterFallback
)(using Executor, Scheduler):

private val config = appConfig.get[PushConfig]("push")(AutoConfig.loader)
Expand Down
5 changes: 3 additions & 2 deletions modules/push/src/main/PushApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ final private class PushApi(
gameRepo: lila.core.game.GameRepo,
namer: lila.core.game.Namer,
notifyAllows: lila.core.notify.GetNotifyAllows,
postApi: lila.core.forum.ForumPostApi
)(using Executor, Scheduler)(using lightUser: LightUser.GetterFallback):
postApi: lila.core.forum.ForumPostApi,
lightUser: lila.core.LightUser.GetterFallback
)(using Executor, Scheduler):

import PushApi.*
import PushApi.Data.payload
Expand Down
5 changes: 2 additions & 3 deletions modules/report/src/main/ReportApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ final class ReportApi(
isOnline: lila.core.socket.IsOnline,
cacheApi: lila.memo.CacheApi,
snoozer: lila.memo.Snoozer[Report.SnoozeKey],
thresholds: Thresholds,
domain: lila.core.config.NetDomain
)(using Executor, Scheduler)
thresholds: Thresholds
)(using Executor, Scheduler, lila.core.config.NetDomain)
extends lila.core.report.ReportApi:

import BSONHandlers.given
Expand Down
4 changes: 2 additions & 2 deletions modules/round/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ final class Env(
appConfig: Configuration,
db: lila.db.Db,
gameRepo: GameRepo,
idGenerator: lila.game.IdGenerator,
userRepo: lila.user.UserRepo,
userApi: lila.user.UserApi,
chatApi: lila.chat.ChatApi,
Expand All @@ -56,7 +55,8 @@ final class Env(
Executor,
akka.stream.Materializer,
lila.core.i18n.Translator,
lila.core.config.RateLimit
lila.core.config.RateLimit,
lila.game.IdGenerator
):

private val (botSync, async) = (lightUserApi.isBotSync, lightUserApi.async)
Expand Down
4 changes: 3 additions & 1 deletion modules/security/src/main/UserAgentParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ object UserAgentParser:
def isSuspicious(req: RequestHeader): Boolean = HTTPRequest.userAgent(req).forall(isSuspicious)

def isSuspicious(ua: UA): Boolean =
!isLichobile(ua) && {
!isLichobile(ua) && !isLM(ua) && {
val str = ua.value.take(200).toLowerCase
str.lengthIs < 30 || isMacOsEdge(str) || !popularBrowser(str)
}
Expand All @@ -66,3 +66,5 @@ object UserAgentParser:
private def isMacOsEdge(ua: String) = ua.contains("macintosh") && ua.contains("edg/")

private def isLichobile(ua: UA) = ua.value.contains("Lichobile/")

private def isLM(ua: UA) = ua.value.startsWith("LM/0.13.13 Android/1")
17 changes: 13 additions & 4 deletions modules/security/src/main/UserTrust.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ private final class UserTrustApi(
userRepo
.byId(id)
.flatMapz: user =>
if hasHistory(user) then fuccess(true)
if hasHistory(user)
then
lila.mon.security.userTrust(true, "history")
fuccess(true)
else if looksLikeKnownAbuser(user)
then
logger.info(s"Not trusting user $id because of suspicious metadata")
lila.mon.security.userTrust(false, "metadata")
fuccess(false)
else
sessionStore
Expand All @@ -32,15 +36,20 @@ private final class UserTrustApi(
sessions.map(_.ua).find(UserAgentParser.trust.isSuspicious) match
case Some(ua) =>
logger.info(s"Not trusting user $id because of suspicious user agent: $ua")
lila.mon.security.userTrust(false, "ua")
fuccess(false)
case None =>
sessions
.map(_.ip)
.findM(ipTrust.isSuspicious)
.map: found =>
found.foreach: ip =>
.map:
case Some(ip) =>
logger.info(s"Not trusting user $id because of suspicious IP: $ip")
found.isEmpty
lila.mon.security.userTrust(false, "ip")
false
case None =>
lila.mon.security.userTrust(true, "new")
true

private def hasHistory(user: User): Boolean =
user.count.lossH > 10 || user.createdSinceDays(15) || !user.plan.isEmpty || user.hasTitle
Expand Down
3 changes: 3 additions & 0 deletions modules/security/src/test/UserAgentParserTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class UserAgentTrustTest extends munit.FunSuite:
assert:
!susp:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"
assert:
!susp:
"LM/0.13.13 Android/14 SM-S916N"

test("susp"):
assert:
Expand Down
Loading

0 comments on commit 8de55a8

Please sign in to comment.