migration WIP
parent
b23e862015
commit
29a411ad38
|
@ -33,12 +33,12 @@ final class BookmarkApi(
|
|||
}
|
||||
|
||||
def removeByGameId(gameId: Game.ID): Funit =
|
||||
coll.remove($doc("g" -> gameId)).void
|
||||
coll.delete.one($doc("g" -> gameId)).void
|
||||
|
||||
def removeByGameIds(gameIds: List[Game.ID]): Funit =
|
||||
coll.remove($doc("g" $in gameIds)).void
|
||||
coll.delete.one($doc("g" $in gameIds)).void
|
||||
|
||||
def remove(gameId: Game.ID, userId: User.ID): Funit = coll.remove(selectId(gameId, userId)).void
|
||||
def remove(gameId: Game.ID, userId: User.ID): Funit = coll.delete.one(selectId(gameId, userId)).void
|
||||
// def remove(selector: Bdoc): Funit = coll.remove(selector).void
|
||||
|
||||
def toggle(gameId: Game.ID, userId: User.ID): Funit =
|
||||
|
@ -54,7 +54,7 @@ final class BookmarkApi(
|
|||
paginator.byUser(user, page) map2 { (b: Bookmark) => b.game }
|
||||
|
||||
private def add(gameId: Game.ID, userId: User.ID, date: DateTime): Funit =
|
||||
coll.insert($doc(
|
||||
coll.insert.one($doc(
|
||||
"_id" -> makeId(gameId, userId),
|
||||
"g" -> gameId,
|
||||
"u" -> userId,
|
||||
|
|
|
@ -69,7 +69,7 @@ object ChatTimeout {
|
|||
val all: List[Reason] = List(PublicShaming, Insult, Spam, Other)
|
||||
def apply(key: String) = all.find(_.key == key)
|
||||
}
|
||||
implicit val ReasonBSONHandler: BSONHandler[Reason] = lila.db.BSON.tryHandler[Reason](
|
||||
implicit val ReasonBSONHandler: BSONHandler[Reason] = tryHandler[Reason](
|
||||
{ case BSONString(value) => Reason(value) toTry s"Invalid reason ${value}" },
|
||||
x => BSONString(x.key)
|
||||
)
|
||||
|
|
|
@ -42,18 +42,18 @@ object Line {
|
|||
val textMaxSize = 140
|
||||
val titleSep = '~'
|
||||
|
||||
import reactivemongo.api.bson.{ BSONHandler, BSONString }
|
||||
import reactivemongo.api.bson._
|
||||
|
||||
private val invalidLine = UserLine("", None, "[invalid character]", troll = false, deleted = true)
|
||||
|
||||
private[chat] implicit val userLineBSONHandler = lila.db.BSON.quickHandler[UserLine](
|
||||
{ case BSONString(value) => strToUserLine(value) getOrElse invalidLine },
|
||||
x => BSONString(userLineToStr(x))
|
||||
private[chat] implicit val userLineBSONHandler = BSONStringHandler.as[UserLine](
|
||||
v => strToUserLine(v) getOrElse invalidLine,
|
||||
userLineToStr
|
||||
)
|
||||
|
||||
private[chat] implicit val lineBSONHandler = lila.db.BSON.quickHandler[Line](
|
||||
{ case BSONString(value) => strToLine(value) getOrElse invalidLine },
|
||||
x => BSONString(lineToStr(x))
|
||||
private[chat] implicit val lineBSONHandler = BSONStringHandler.as[Line](
|
||||
v => strToLine(v) getOrElse invalidLine,
|
||||
lineToStr
|
||||
)
|
||||
|
||||
private val UserLineRegex = """(?s)([\w-~]{2,}+)([ !?])(.++)""".r
|
||||
|
|
|
@ -4,7 +4,6 @@ import org.joda.time.DateTime
|
|||
import ornicar.scalalib.Zero
|
||||
import reactivemongo.api.bson._
|
||||
import reactivemongo.api.bson.compat._
|
||||
import reactivemongo.api.bson.exceptions.TypeDoesNotMatchException
|
||||
import scala.util.{ Try, Success, Failure }
|
||||
|
||||
import dsl._
|
||||
|
@ -42,91 +41,6 @@ abstract class BSONReadOnly[T] extends BSONDocumentReader[T] {
|
|||
|
||||
object BSON extends Handlers {
|
||||
|
||||
// def toDocHandler[A](implicit handler: BSONHandler[A]): BSONDocumentHandler[A] =
|
||||
// new BSONDocumentReader[A] with BSONDocumentWriter[A] with BSONHandler[A] {
|
||||
// def read(doc: BSONDocument) = handler read doc
|
||||
// def write(o: A) = handler write o
|
||||
// }
|
||||
|
||||
// object MapDocument {
|
||||
|
||||
// implicit def MapReader[K, V](implicit kIso: Iso.StringIso[K], vr: BSONDocumentReader[V]): BSONDocumentReader[Map[K, V]] = new BSONDocumentReader[Map[K, V]] {
|
||||
// def read(bson: Bdoc): Map[K, V] = {
|
||||
// // mutable optimized implementation
|
||||
// val b = Map.newBuilder[K, V]
|
||||
// for (tuple <- bson.elements)
|
||||
// // assume that all values in the document are Bdocs
|
||||
// b += (kIso.from(tuple.name) -> vr.read(tuple.value.asInstanceOf[Bdoc]))
|
||||
// b.result
|
||||
// }
|
||||
// }
|
||||
|
||||
// implicit def MapWriter[K, V](implicit kIso: Iso.StringIso[K], vw: reactivemongo.api.bson.BSONDocumentWriter[V]): BSONDocumentWriter[Map[K, V]] = new BSONDocumentWriter[Map[K, V]] {
|
||||
// def write(map: Map[K, V]): Bdoc = BSONDocument {
|
||||
// map.map { tuple =>
|
||||
// kIso.to(tuple._1) -> vw.writeTry(tuple._2).get
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// implicit def MapHandler[K: Iso.StringIso, V: BSONDocumentHandler]: BSONHandler[Map[K, V]] = new BSONHandler[Map[K, V]] {
|
||||
// private val reader = MapReader[K, V]
|
||||
// private val writer = MapWriter[K, V]
|
||||
// def read(bson: Bdoc): Map[K, V] = reader read bson
|
||||
// def write(map: Map[K, V]): Bdoc = writer write map
|
||||
// }
|
||||
// }
|
||||
|
||||
// object MapValue {
|
||||
|
||||
// implicit def MapReader[K, V](implicit kIso: Iso.StringIso[K], vr: BSONReader[V]): BSONDocumentReader[Map[K, V]] = new BSONDocumentReader[Map[K, V]] {
|
||||
// def read(bson: Bdoc): Map[K, V] = {
|
||||
// val valueReader = vr.asInstanceOf[BSONReader[V]]
|
||||
// // mutable optimized implementation
|
||||
// val b = Map.newBuilder[K, V]
|
||||
// for (tuple <- bson.elements) b += (kIso.from(tuple.name) -> valueReader.read(tuple.value))
|
||||
// b.result
|
||||
// }
|
||||
// }
|
||||
|
||||
// implicit def MapWriter[K, V](implicit kIso: Iso.StringIso[K], vw: BSONWriter[V]): BSONDocumentWriter[Map[K, V]] = new BSONDocumentWriter[Map[K, V]] {
|
||||
// def write(map: Map[K, V]): Bdoc = BSONDocument {
|
||||
// map.toStream.map { tuple =>
|
||||
// kIso.to(tuple._1) -> vw.writeTry(tuple._2).get
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// implicit def MapHandler[K, V](implicit kIso: Iso.StringIso[K], vr: BSONReader[V], vw: BSONWriter[V]): BSONHandler[Map[K, V]] = new BSONHandler[Map[K, V]] {
|
||||
// private val reader = MapReader[K, V]
|
||||
// private val writer = MapWriter[K, V]
|
||||
// def read(bson: Bdoc): Map[K, V] = reader read bson
|
||||
// def write(map: Map[K, V]): Bdoc = writer write map
|
||||
// }
|
||||
// }
|
||||
|
||||
def quickHandler[T](read: PartialFunction[BSONValue, T], write: T => BSONValue): BSONHandler[T] = new BSONHandler[T] {
|
||||
def readTry(bson: BSONValue) = read.andThen(Success(_)).applyOrElse(
|
||||
bson,
|
||||
(b: BSONValue) => handlerBadType(b)
|
||||
)
|
||||
def writeTry(t: T) = Success(write(t))
|
||||
}
|
||||
|
||||
def tryHandler[T](read: PartialFunction[BSONValue, Try[T]], write: T => BSONValue): BSONHandler[T] = new BSONHandler[T] {
|
||||
def readTry(bson: BSONValue) = read.applyOrElse(
|
||||
bson,
|
||||
(b: BSONValue) => handlerBadType(b)
|
||||
)
|
||||
def writeTry(t: T) = Success(write(t))
|
||||
}
|
||||
|
||||
def handlerBadType[T](b: BSONValue): Try[T] =
|
||||
Failure(TypeDoesNotMatchException("BSONBinary", b.getClass.getSimpleName))
|
||||
|
||||
def handlerBadValue[T](msg: String): Try[T] =
|
||||
Failure(new IllegalArgumentException(msg))
|
||||
|
||||
final class Reader(val doc: Bdoc) {
|
||||
|
||||
val map = {
|
||||
|
|
|
@ -24,7 +24,7 @@ object ByteArray {
|
|||
def fromHexStr(hexStr: String): Try[ByteArray] =
|
||||
Try(ByteArray(hex str2Hex hexStr))
|
||||
|
||||
implicit val ByteArrayBSONHandler = lila.db.BSON.quickHandler[ByteArray](
|
||||
implicit val ByteArrayBSONHandler = dsl.quickHandler[ByteArray](
|
||||
{ case v: BSONBinary => ByteArray(v.byteArray) },
|
||||
v => BSONBinary(v.value, subtype)
|
||||
)
|
||||
|
|
|
@ -13,9 +13,9 @@ import dsl.Coll
|
|||
import lila.common.Chronometer
|
||||
import lila.common.config._
|
||||
|
||||
case class DbConfig(
|
||||
uri: String,
|
||||
@ConfigName("image.collection") imageCollName: Option[CollName] = None
|
||||
class DbConfig(
|
||||
val uri: String,
|
||||
@ConfigName("image.collection") val imageCollName: Option[CollName] = None
|
||||
)
|
||||
|
||||
final class Env(name: String, config: DbConfig) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package lila.db
|
|||
|
||||
import org.joda.time.DateTime
|
||||
import reactivemongo.api.bson._
|
||||
import reactivemongo.api.bson.exceptions.TypeDoesNotMatchException
|
||||
import scala.util.{ Try, Success, Failure }
|
||||
import scalaz.NonEmptyList
|
||||
|
||||
|
@ -10,7 +11,7 @@ import lila.common.{ Iso, IpAddress, EmailAddress, NormalizedEmailAddress }
|
|||
|
||||
trait Handlers {
|
||||
|
||||
implicit val BSONJodaDateTimeHandler = lila.db.BSON.quickHandler[DateTime](
|
||||
implicit val BSONJodaDateTimeHandler = dsl.quickHandler[DateTime](
|
||||
{ case v: BSONDateTime => new DateTime(v.value) },
|
||||
v => BSONDateTime(v.getMillis)
|
||||
)
|
||||
|
@ -36,11 +37,33 @@ trait Handlers {
|
|||
|
||||
def dateIsoHandler[A](implicit iso: Iso[DateTime, A]): BSONHandler[A] = isoHandler[A, DateTime](iso)
|
||||
|
||||
implicit def bsonArrayToListHandler[T](implicit handler: BSONHandler[T]): BSONHandler[List[T]] =
|
||||
lila.db.BSON.quickHandler[List[T]](
|
||||
{ case BSONArray(values) => values.view.flatMap(handler.readOpt).to(List) },
|
||||
repr => BSONArray(repr.flatMap(handler.writeOpt))
|
||||
def quickHandler[T](read: PartialFunction[BSONValue, T], write: T => BSONValue): BSONHandler[T] = new BSONHandler[T] {
|
||||
def readTry(bson: BSONValue) = read.andThen(Success(_)).applyOrElse(
|
||||
bson,
|
||||
(b: BSONValue) => handlerBadType(b)
|
||||
)
|
||||
def writeTry(t: T) = Success(write(t))
|
||||
}
|
||||
|
||||
def tryHandler[T](read: PartialFunction[BSONValue, Try[T]], write: T => BSONValue): BSONHandler[T] = new BSONHandler[T] {
|
||||
def readTry(bson: BSONValue) = read.applyOrElse(
|
||||
bson,
|
||||
(b: BSONValue) => handlerBadType(b)
|
||||
)
|
||||
def writeTry(t: T) = Success(write(t))
|
||||
}
|
||||
|
||||
def handlerBadType[T](b: BSONValue): Try[T] =
|
||||
Failure(TypeDoesNotMatchException("BSONBinary", b.getClass.getSimpleName))
|
||||
|
||||
def handlerBadValue[T](msg: String): Try[T] =
|
||||
Failure(new IllegalArgumentException(msg))
|
||||
|
||||
// implicit def bsonArrayToListHandler[T](implicit handler: BSONHandler[T]): BSONHandler[List[T]] =
|
||||
// dsl.quickHandler[List[T]](
|
||||
// { case BSONArray(values) => values.view.flatMap(handler.readOpt).to(List) },
|
||||
// repr => BSONArray(repr.flatMap(handler.writeOpt))
|
||||
// )
|
||||
|
||||
// implicit def bsonArrayToVectorHandler[T](implicit handler: BSONHandler[T]): BSONHandler[Vector[T]] = new BSONHandler[Vector[T]] {
|
||||
// def read(array: BSONArray) = array.values.view.flatMap(handler.readOpt).to(Vector)
|
||||
|
|
|
@ -49,7 +49,7 @@ private object BSONHandlers {
|
|||
})
|
||||
}
|
||||
|
||||
implicit val EntryIdHandler = lila.db.BSON.tryHandler[Id](
|
||||
implicit val EntryIdHandler = tryHandler[Id](
|
||||
{
|
||||
case BSONString(value) =>
|
||||
value split ':' match {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package lila.evalCache
|
||||
|
||||
import scala.math.Ordering.Float.TotalOrdering
|
||||
|
||||
import EvalCacheEntry._
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,12 +20,12 @@ object BSONHandlers {
|
|||
def writeTry(cc: CheckCount) = Success(BSONArray(cc.white, cc.black))
|
||||
}
|
||||
|
||||
implicit val StatusBSONHandler = lila.db.BSON.tryHandler[Status](
|
||||
implicit val StatusBSONHandler = tryHandler[Status](
|
||||
{ case BSONInteger(v) => Status(v) toTry s"No such status: $v" },
|
||||
x => BSONInteger(x.id)
|
||||
)
|
||||
|
||||
private[game] implicit val unmovedRooksHandler = lila.db.BSON.tryHandler[UnmovedRooks](
|
||||
private[game] implicit val unmovedRooksHandler = tryHandler[UnmovedRooks](
|
||||
{ case bin: BSONBinary => ByteArrayBSONHandler.readTry(bin) map BinaryFormat.unmovedRooks.read },
|
||||
x => ByteArrayBSONHandler.writeTry(BinaryFormat.unmovedRooks write x).get
|
||||
)
|
||||
|
|
|
@ -44,7 +44,7 @@ object Blurs {
|
|||
|
||||
import reactivemongo.api.bson._
|
||||
|
||||
private[game] implicit val BlursBitsBSONHandler = lila.db.BSON.tryHandler[Bits](
|
||||
private[game] implicit val BlursBitsBSONHandler = lila.db.dsl.tryHandler[Bits](
|
||||
{
|
||||
case BSONInteger(bits) => Success(Bits(bits & 0xffffffffL))
|
||||
case BSONLong(bits) => Success(Bits(bits))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package lila.notify
|
||||
|
||||
import chess.Color
|
||||
import lila.db.BSON.{ Reader, Writer }
|
||||
import lila.db.dsl._
|
||||
import lila.db.{ dsl, BSON }
|
||||
|
@ -52,10 +53,7 @@ private object BSONHandlers {
|
|||
implicit val IrwinDoneHandler = Macros.handler[IrwinDone]
|
||||
implicit val GenericLinkHandler = Macros.handler[GenericLink]
|
||||
|
||||
implicit val ColorBSONHandler = lila.db.BSON.quickHandler[chess.Color](
|
||||
{ case BSONBoolean(v) => chess.Color(v) },
|
||||
c => BSONBoolean(c.white)
|
||||
)
|
||||
implicit val ColorBSONHandler = BSONBooleanHandler.as[Color](Color.apply, _.white)
|
||||
|
||||
implicit val NotificationContentHandler = new BSON[NotificationContent] {
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ object OAuthScope {
|
|||
|
||||
import reactivemongo.api.bson._
|
||||
import lila.db.dsl._
|
||||
private[oauth] implicit val scopeHandler = lila.db.BSON.tryHandler[OAuthScope](
|
||||
private[oauth] implicit val scopeHandler = tryHandler[OAuthScope](
|
||||
{ case b: BSONString => OAuthScope.byKey.get(b.value) toTry s"No such scope: ${b.value}" },
|
||||
s => BSONString(s.key)
|
||||
)
|
||||
|
|
|
@ -47,14 +47,14 @@ private[puzzle] final class Daily(
|
|||
none
|
||||
}
|
||||
|
||||
private def findCurrent = coll.find(
|
||||
private def findCurrent = coll.ext.find(
|
||||
$doc(F.day $gt DateTime.now.minusMinutes(24 * 60 - 15))
|
||||
).uno[Puzzle]
|
||||
|
||||
private def findNew = coll.find(
|
||||
private def findNew = coll.ext.find(
|
||||
$doc(F.day $exists false, F.voteNb $gte 200)
|
||||
).sort($doc(F.voteRatio -> -1)).uno[Puzzle] flatMap {
|
||||
case Some(puzzle) => coll.update(
|
||||
case Some(puzzle) => coll.update.one(
|
||||
$id(puzzle.id),
|
||||
$set(F.day -> DateTime.now)
|
||||
) inject puzzle.some
|
||||
|
|
|
@ -1,50 +1,57 @@
|
|||
package lila.puzzle
|
||||
|
||||
import akka.actor.{ ActorSelection, ActorSystem }
|
||||
import com.typesafe.config.Config
|
||||
import com.softwaremill.macwire._
|
||||
import io.methvin.play.autoconfig._
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
import play.api.Configuration
|
||||
|
||||
import lila.common.config._
|
||||
import lila.db.Env.configLoader
|
||||
|
||||
@Module
|
||||
private class CoachConfig(
|
||||
val mongodb: lila.db.DbConfig,
|
||||
@ConfigName("collection.puzzle") val puzzleColl: CollName,
|
||||
@ConfigName("collection.round") val roundColl: CollName,
|
||||
@ConfigName("collection.vote") val voteColl: CollName,
|
||||
@ConfigName("collection.head") val headColl: CollName,
|
||||
@ConfigName("api.token") val apiToken: Secret,
|
||||
@ConfigName("animation.duration") val animationDuration: FiniteDuration,
|
||||
@ConfigName("selector.puzzle_id_min") val puzzleIdMin: Int
|
||||
)
|
||||
|
||||
final class Env(
|
||||
config: Config,
|
||||
appConfig: Configuration,
|
||||
renderer: ActorSelection,
|
||||
historyApi: lila.history.HistoryApi,
|
||||
lightUserApi: lila.user.LightUserApi,
|
||||
asyncCache: lila.memo.AsyncCache.Builder,
|
||||
system: ActorSystem,
|
||||
lifecycle: play.api.inject.ApplicationLifecycle
|
||||
) {
|
||||
gameRepo: lila.game.GameRepo,
|
||||
userRepo: lila.user.UserRepo
|
||||
)(implicit system: ActorSystem) {
|
||||
|
||||
private val settings = new {
|
||||
val CollectionPuzzle = config getString "collection.puzzle"
|
||||
val CollectionRound = config getString "collection.round"
|
||||
val CollectionVote = config getString "collection.vote"
|
||||
val CollectionHead = config getString "collection.head"
|
||||
val ApiToken = config getString "api.token"
|
||||
val AnimationDuration = config duration "animation.duration"
|
||||
val PuzzleIdMin = config getInt "selector.puzzle_id_min"
|
||||
}
|
||||
import settings._
|
||||
private val config = appConfig.get[CoachConfig]("coach")(AutoConfig.loader)
|
||||
|
||||
private val db = new lila.db.Env("puzzle", config getConfig "mongodb", lifecycle)
|
||||
private lazy val db = new lila.db.Env("puzzle", config.mongodb)
|
||||
|
||||
private lazy val gameJson = new GameJson(asyncCache, lightUserApi)
|
||||
private lazy val gameJson = wire[GameJson]
|
||||
|
||||
lazy val jsonView = new JsonView(
|
||||
gameJson,
|
||||
animationDuration = AnimationDuration
|
||||
)
|
||||
lazy val jsonView = wire[JsonView]
|
||||
|
||||
lazy val api = new PuzzleApi(
|
||||
puzzleColl = puzzleColl,
|
||||
roundColl = roundColl,
|
||||
voteColl = voteColl,
|
||||
headColl = headColl,
|
||||
puzzleIdMin = PuzzleIdMin,
|
||||
puzzleIdMin = config.puzzleIdMin,
|
||||
asyncCache = asyncCache,
|
||||
apiToken = ApiToken
|
||||
apiToken = config.apiToken
|
||||
)
|
||||
|
||||
lazy val finisher = new Finisher(
|
||||
historyApi = historyApi,
|
||||
userRepo = userRepo,
|
||||
api = api,
|
||||
puzzleColl = puzzleColl
|
||||
)
|
||||
|
@ -52,14 +59,14 @@ final class Env(
|
|||
lazy val selector = new Selector(
|
||||
puzzleColl = puzzleColl,
|
||||
api = api,
|
||||
puzzleIdMin = PuzzleIdMin
|
||||
puzzleIdMin = config.puzzleIdMin
|
||||
)
|
||||
|
||||
lazy val batch = new PuzzleBatch(
|
||||
puzzleColl = puzzleColl,
|
||||
api = api,
|
||||
finisher = finisher,
|
||||
puzzleIdMin = PuzzleIdMin
|
||||
puzzleIdMin = config.puzzleIdMin
|
||||
)
|
||||
|
||||
lazy val userInfos = new UserInfosApi(
|
||||
|
@ -89,21 +96,8 @@ final class Env(
|
|||
}
|
||||
}
|
||||
|
||||
private[puzzle] lazy val puzzleColl = db(CollectionPuzzle)
|
||||
private[puzzle] lazy val roundColl = db(CollectionRound)
|
||||
private[puzzle] lazy val voteColl = db(CollectionVote)
|
||||
private[puzzle] lazy val headColl = db(CollectionHead)
|
||||
}
|
||||
|
||||
object Env {
|
||||
|
||||
lazy val current: Env = "puzzle" boot new Env(
|
||||
config = lila.common.PlayApp loadConfig "puzzle",
|
||||
renderer = lila.hub.Env.current.renderer,
|
||||
historyApi = lila.history.Env.current.api,
|
||||
lightUserApi = lila.user.Env.current.lightUserApi,
|
||||
asyncCache = lila.memo.Env.current.asyncCache,
|
||||
system = lila.common.PlayApp.system,
|
||||
lifecycle = lila.common.PlayApp.lifecycle
|
||||
)
|
||||
private lazy val puzzleColl = db(config.puzzleColl)
|
||||
private lazy val roundColl = db(config.roundColl)
|
||||
private lazy val voteColl = db(config.voteColl)
|
||||
private lazy val headColl = db(config.headColl)
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
package lila.puzzle
|
||||
|
||||
import org.goochjs.glicko2._
|
||||
import org.goochjs.glicko2.{ Rating, RatingCalculator, RatingPeriodResults }
|
||||
import org.joda.time.DateTime
|
||||
|
||||
import chess.Mode
|
||||
import lila.common.Bus
|
||||
import lila.db.dsl._
|
||||
import lila.rating.{ Glicko, PerfType }
|
||||
import lila.common.Bus
|
||||
import lila.user.{ User, UserRepo }
|
||||
|
||||
private[puzzle] final class Finisher(
|
||||
api: PuzzleApi,
|
||||
userRepo: UserRepo,
|
||||
historyApi: lila.history.HistoryApi,
|
||||
puzzleColl: Coll
|
||||
) {
|
||||
|
@ -35,11 +36,11 @@ private[puzzle] final class Finisher(
|
|||
)
|
||||
historyApi.addPuzzle(user = user, completedAt = date, perf = userPerf)
|
||||
(api.round upsert round) >> {
|
||||
puzzleColl.update(
|
||||
puzzleColl.update.one(
|
||||
$id(puzzle.id),
|
||||
$inc(Puzzle.BSONFields.attempts -> $int(1)) ++
|
||||
$set(Puzzle.BSONFields.perf -> PuzzlePerf.puzzlePerfBSONHandler.write(puzzlePerf))
|
||||
) zip UserRepo.setPerf(user.id, PerfType.Puzzle, userPerf)
|
||||
) zip userRepo.setPerf(user.id, PerfType.Puzzle, userPerf)
|
||||
} inject {
|
||||
Bus.publish(Puzzle.UserResult(puzzle.id, user.id, result, formerUserRating -> userPerf.intRating), "finishPuzzle")
|
||||
round -> Mode.Rated
|
||||
|
@ -77,7 +78,7 @@ private[puzzle] final class Finisher(
|
|||
ratingDiff = userPerf.intRating - formerUserRating
|
||||
)
|
||||
(api.round add a) >>
|
||||
UserRepo.setPerf(user.id, PerfType.Puzzle, userPerf) >>-
|
||||
userRepo.setPerf(user.id, PerfType.Puzzle, userPerf) >>-
|
||||
Bus.publish(
|
||||
Puzzle.UserResult(puzzle.id, user.id, result, formerUserRating -> userPerf.intRating),
|
||||
"finishPuzzle"
|
||||
|
|
|
@ -7,6 +7,7 @@ import lila.game.{ Game, GameRepo, PerfPicker }
|
|||
import lila.tree.Node.{ partitionTreeJsonWriter, minimalNodeJsonWriter }
|
||||
|
||||
private final class GameJson(
|
||||
gameRepo: GameRepo,
|
||||
asyncCache: lila.memo.AsyncCache.Builder,
|
||||
lightUserApi: lila.user.LightUserApi
|
||||
) {
|
||||
|
@ -28,7 +29,7 @@ private final class GameJson(
|
|||
|
||||
private def generate(ck: CacheKey): Fu[JsObject] = ck match {
|
||||
case CacheKey(gameId, plies, onlyLast) =>
|
||||
(GameRepo game gameId).flatten(s"Missing puzzle game $gameId!") flatMap {
|
||||
gameRepo game gameId orFail s"Missing puzzle game $gameId!" flatMap {
|
||||
generate(_, plies, onlyLast)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,11 @@ import play.api.libs.json._
|
|||
|
||||
import lila.game.GameRepo
|
||||
import lila.tree
|
||||
import lila.tree.Node.defaultNodeJsonWriter
|
||||
|
||||
final class JsonView(
|
||||
gameJson: GameJson,
|
||||
gameRepo: GameRepo,
|
||||
animationDuration: scala.concurrent.duration.Duration
|
||||
) {
|
||||
|
||||
|
@ -66,7 +68,7 @@ final class JsonView(
|
|||
)
|
||||
|
||||
def batch(puzzles: List[Puzzle], userInfos: UserInfos): Fu[JsObject] = for {
|
||||
games <- GameRepo.gameOptionsFromSecondary(puzzles.map(_.gameId))
|
||||
games <- gameRepo.gameOptionsFromSecondary(puzzles.map(_.gameId))
|
||||
jsons <- (puzzles zip games).collect {
|
||||
case (puzzle, Some(game)) =>
|
||||
gameJson.noCache(game, puzzle.initialPly, true) map { gameJson =>
|
||||
|
@ -92,7 +94,7 @@ final class JsonView(
|
|||
"lines" -> lila.puzzle.Line.toJson(puzzle.lines),
|
||||
"vote" -> puzzle.vote.sum
|
||||
).add("initialMove" -> isOldMobile.option(puzzle.initialMove.uci))
|
||||
.add("branch" -> (!isOldMobile).option(makeBranch(puzzle)))
|
||||
.add("branch" -> (!isOldMobile).??(makeBranch(puzzle)).map(defaultNodeJsonWriter.writes))
|
||||
.add("enabled" -> puzzle.enabled)
|
||||
|
||||
private def makeBranch(puzzle: Puzzle): Option[tree.Branch] = {
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
package lila.puzzle
|
||||
|
||||
import scala.collection.breakOut
|
||||
|
||||
import chess.Color
|
||||
import chess.format.{ Uci, Forsyth }
|
||||
import org.joda.time.DateTime
|
||||
import scala.util.{ Try, Success, Failure }
|
||||
|
||||
case class Puzzle(
|
||||
id: PuzzleId,
|
||||
|
@ -77,31 +76,29 @@ object Puzzle {
|
|||
import reactivemongo.api.bson._
|
||||
import lila.db.BSON
|
||||
import BSON.BSONJodaDateTimeHandler
|
||||
private implicit val lineBSONHandler = new BSONHandler[BSONDocument, Lines] {
|
||||
private implicit val linesBSONHandler = new BSONDocumentReader[Lines] with BSONDocumentWriter[Lines] with BSONHandler[Lines] {
|
||||
private def readMove(move: String) = chess.Pos.doublePiotrToKey(move take 2) match {
|
||||
case Some(m) => s"$m${move drop 2}"
|
||||
case _ => sys error s"Invalid piotr move notation: $move"
|
||||
}
|
||||
def read(doc: BSONDocument): Lines = doc.elements.map {
|
||||
case BSONElement(move, BSONBoolean(true)) => Win(readMove(move))
|
||||
|
||||
case BSONElement(move, BSONBoolean(false)) => Retry(readMove(move))
|
||||
|
||||
case BSONElement(move, more: BSONDocument) =>
|
||||
Node(readMove(move), read(more))
|
||||
|
||||
case BSONElement(move, value) =>
|
||||
throw new Exception(s"Can't read value of $move: $value")
|
||||
}(breakOut)
|
||||
def readDocument(doc: BSONDocument): Try[Lines] = Try {
|
||||
doc.elements.map {
|
||||
case BSONElement(move, BSONBoolean(true)) => Win(readMove(move))
|
||||
case BSONElement(move, BSONBoolean(false)) => Retry(readMove(move))
|
||||
case BSONElement(move, more: BSONDocument) => Node(readMove(move), readDocument(more).get)
|
||||
case BSONElement(move, value) =>
|
||||
throw new Exception(s"Can't read value of $move: $value")
|
||||
} to List
|
||||
}
|
||||
private def writeMove(move: String) = chess.Pos.doubleKeyToPiotr(move take 4) match {
|
||||
case Some(m) => s"$m${move drop 4}"
|
||||
case _ => sys error s"Invalid move notation: $move"
|
||||
}
|
||||
def write(lines: Lines): BSONDocument = BSONDocument(lines map {
|
||||
def writeTry(lines: Lines): Try[BSONDocument] = Success(BSONDocument(lines map {
|
||||
case Win(move) => writeMove(move) -> BSONBoolean(true)
|
||||
case Retry(move) => writeMove(move) -> BSONBoolean(false)
|
||||
case Node(move, lines) => writeMove(move) -> write(lines)
|
||||
})
|
||||
case Node(move, lines) => writeMove(move) -> writeTry(lines).get
|
||||
}))
|
||||
}
|
||||
|
||||
object BSONFields {
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
package lila.puzzle
|
||||
|
||||
import org.joda.time.DateTime
|
||||
import play.api.libs.iteratee._
|
||||
import play.api.libs.json._
|
||||
import reactivemongo.api.ReadPreference
|
||||
import reactivemongo.play.iteratees.cursorProducer
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.common.MaxPerSecond
|
||||
import lila.common.config.MaxPerSecond
|
||||
import lila.db.dsl._
|
||||
import lila.user.User
|
||||
|
||||
|
@ -16,66 +14,66 @@ final class PuzzleActivity(
|
|||
roundColl: Coll
|
||||
)(implicit system: akka.actor.ActorSystem) {
|
||||
|
||||
import PuzzleActivity._
|
||||
import Round.RoundBSONHandler
|
||||
// import PuzzleActivity._
|
||||
// import Round.RoundBSONHandler
|
||||
|
||||
def stream(config: Config): Enumerator[String] = {
|
||||
// def stream(config: Config): Enumerator[String] = {
|
||||
|
||||
val selector = $doc("_id" $startsWith config.user.id)
|
||||
// val selector = $doc("_id" $startsWith config.user.id)
|
||||
|
||||
val query = roundColl.find(selector).sort($sort desc "_id")
|
||||
// val query = roundColl.find(selector).sort($sort desc "_id")
|
||||
|
||||
val infinite = query.copy(options = query.options.batchSize(config.perSecond.value))
|
||||
.cursor[Round](ReadPreference.secondaryPreferred)
|
||||
.bulkEnumerator() &>
|
||||
Enumeratee.mapM[Iterator[Round]].apply[Seq[JsObject]] { rounds =>
|
||||
enrich(rounds.toSeq)
|
||||
} &>
|
||||
lila.common.Iteratee.delay(1 second) &>
|
||||
Enumeratee.mapConcat(_.toSeq)
|
||||
// val infinite = query.copy(options = query.options.batchSize(config.perSecond.value))
|
||||
// .cursor[Round](ReadPreference.secondaryPreferred)
|
||||
// .bulkEnumerator() &>
|
||||
// Enumeratee.mapM[Iterator[Round]].apply[Seq[JsObject]] { rounds =>
|
||||
// enrich(rounds.toSeq)
|
||||
// } &>
|
||||
// lila.common.Iteratee.delay(1 second) &>
|
||||
// Enumeratee.mapConcat(_.toSeq)
|
||||
|
||||
val stream = config.max.fold(infinite) { max =>
|
||||
// I couldn't figure out how to do it properly :( :( :(
|
||||
// the nb can't be set as bulkEnumerator(nb)
|
||||
// because games are further filtered after being fetched
|
||||
var nb = 0
|
||||
infinite &> Enumeratee.mapInput { in =>
|
||||
nb = nb + 1
|
||||
if (nb <= max) in
|
||||
else Input.EOF
|
||||
}
|
||||
}
|
||||
// val stream = config.max.fold(infinite) { max =>
|
||||
// // I couldn't figure out how to do it properly :( :( :(
|
||||
// // the nb can't be set as bulkEnumerator(nb)
|
||||
// // because games are further filtered after being fetched
|
||||
// var nb = 0
|
||||
// infinite &> Enumeratee.mapInput { in =>
|
||||
// nb = nb + 1
|
||||
// if (nb <= max) in
|
||||
// else Input.EOF
|
||||
// }
|
||||
// }
|
||||
|
||||
stream &> formatter
|
||||
}
|
||||
// stream &> formatter
|
||||
// }
|
||||
|
||||
private def enrich(rounds: Seq[Round]): Fu[Seq[JsObject]] =
|
||||
puzzleColl.primitiveMap[Int, Double](
|
||||
ids = rounds.map(_.id.puzzleId).toSeq,
|
||||
field = "perf.gl.r",
|
||||
fieldExtractor = obj => for {
|
||||
perf <- obj.getAs[Bdoc]("perf")
|
||||
gl <- perf.getAs[Bdoc]("gl")
|
||||
rating <- gl.getAs[Double]("r")
|
||||
} yield rating
|
||||
) map { ratings =>
|
||||
rounds.toSeq flatMap { round =>
|
||||
ratings get round.id.puzzleId map { puzzleRating =>
|
||||
Json.obj(
|
||||
"id" -> round.id.puzzleId,
|
||||
"date" -> round.date,
|
||||
"rating" -> round.rating,
|
||||
"ratingDiff" -> round.ratingDiff,
|
||||
"puzzleRating" -> puzzleRating.toInt
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// private def enrich(rounds: Seq[Round]): Fu[Seq[JsObject]] =
|
||||
// puzzleColl.primitiveMap[Int, Double](
|
||||
// ids = rounds.map(_.id.puzzleId).toSeq,
|
||||
// field = "perf.gl.r",
|
||||
// fieldExtractor = obj => for {
|
||||
// perf <- obj.getAs[Bdoc]("perf")
|
||||
// gl <- perf.getAs[Bdoc]("gl")
|
||||
// rating <- gl.getAs[Double]("r")
|
||||
// } yield rating
|
||||
// ) map { ratings =>
|
||||
// rounds.toSeq flatMap { round =>
|
||||
// ratings get round.id.puzzleId map { puzzleRating =>
|
||||
// Json.obj(
|
||||
// "id" -> round.id.puzzleId,
|
||||
// "date" -> round.date,
|
||||
// "rating" -> round.rating,
|
||||
// "ratingDiff" -> round.ratingDiff,
|
||||
// "puzzleRating" -> puzzleRating.toInt
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private def formatter =
|
||||
Enumeratee.map[JsObject].apply[String] { json =>
|
||||
s"${Json.stringify(json)}\n"
|
||||
}
|
||||
// private def formatter =
|
||||
// Enumeratee.map[JsObject].apply[String] { json =>
|
||||
// s"${Json.stringify(json)}\n"
|
||||
// }
|
||||
}
|
||||
|
||||
object PuzzleActivity {
|
||||
|
|
|
@ -4,6 +4,7 @@ import scala.concurrent.duration._
|
|||
|
||||
import play.api.libs.json.JsValue
|
||||
|
||||
import lila.common.config.Secret
|
||||
import lila.db.dsl._
|
||||
import lila.user.User
|
||||
import Puzzle.{ BSONFields => F }
|
||||
|
@ -15,7 +16,7 @@ private[puzzle] final class PuzzleApi(
|
|||
headColl: Coll,
|
||||
puzzleIdMin: PuzzleId,
|
||||
asyncCache: lila.memo.AsyncCache.Builder,
|
||||
apiToken: String
|
||||
apiToken: Secret
|
||||
) {
|
||||
|
||||
import Puzzle.puzzleBSONHandler
|
||||
|
@ -23,13 +24,13 @@ private[puzzle] final class PuzzleApi(
|
|||
object puzzle {
|
||||
|
||||
def find(id: PuzzleId): Fu[Option[Puzzle]] =
|
||||
puzzleColl.find($doc(F.id -> id)).uno[Puzzle]
|
||||
puzzleColl.ext.find($doc(F.id -> id)).uno[Puzzle]
|
||||
|
||||
def findMany(ids: List[PuzzleId]): Fu[List[Option[Puzzle]]] =
|
||||
puzzleColl.optionsByOrderedIds[Puzzle, PuzzleId](ids)(_.id)
|
||||
|
||||
def latest(nb: Int): Fu[List[Puzzle]] =
|
||||
puzzleColl.find($empty)
|
||||
puzzleColl.ext.find($empty)
|
||||
.sort($doc(F.date -> -1))
|
||||
.cursor[Puzzle]()
|
||||
.gather[List](nb)
|
||||
|
@ -41,13 +42,13 @@ private[puzzle] final class PuzzleApi(
|
|||
)
|
||||
|
||||
def export(nb: Int): Fu[List[Puzzle]] = List(true, false).map { mate =>
|
||||
puzzleColl.find($doc(F.mate -> mate))
|
||||
puzzleColl.ext.find($doc(F.mate -> mate))
|
||||
.sort($doc(F.voteRatio -> -1))
|
||||
.cursor[Puzzle]().gather[List](nb / 2)
|
||||
}.sequenceFu.map(_.flatten)
|
||||
|
||||
def disable(id: PuzzleId): Funit =
|
||||
puzzleColl.update(
|
||||
puzzleColl.update.one(
|
||||
$id(id),
|
||||
$doc("$set" -> $doc(F.vote -> AggregateVote.disable))
|
||||
).void
|
||||
|
@ -55,11 +56,11 @@ private[puzzle] final class PuzzleApi(
|
|||
|
||||
object round {
|
||||
|
||||
def add(a: Round) = roundColl insert a
|
||||
def add(a: Round) = roundColl.insert.one(a)
|
||||
|
||||
def upsert(a: Round) = roundColl.update($id(a.id), a, upsert = true)
|
||||
def upsert(a: Round) = roundColl.update.one($id(a.id), a, upsert = true)
|
||||
|
||||
def reset(user: User) = roundColl.remove($doc(
|
||||
def reset(user: User) = roundColl.delete.one($doc(
|
||||
Round.BSONFields.id $startsWith s"${user.id}:"
|
||||
))
|
||||
}
|
||||
|
@ -84,12 +85,12 @@ private[puzzle] final class PuzzleApi(
|
|||
Vote(Vote.makeId(id, user.id), v)
|
||||
)
|
||||
}
|
||||
voteColl.update(
|
||||
voteColl.update.one(
|
||||
$id(v2.id),
|
||||
$set("v" -> v),
|
||||
upsert = true
|
||||
) zip
|
||||
puzzleColl.update(
|
||||
puzzleColl.update.one(
|
||||
$id(p2.id),
|
||||
$set(F.vote -> p2.vote)
|
||||
) map {
|
||||
|
@ -102,7 +103,7 @@ private[puzzle] final class PuzzleApi(
|
|||
|
||||
def find(user: User): Fu[Option[PuzzleHead]] = headColl.byId[PuzzleHead](user.id)
|
||||
|
||||
def set(h: PuzzleHead) = headColl.update($id(h.id), h, upsert = true) void
|
||||
def set(h: PuzzleHead) = headColl.update.one($id(h.id), h, upsert = true) void
|
||||
|
||||
def addNew(user: User, puzzleId: PuzzleId) = set(PuzzleHead(user.id, puzzleId.some, puzzleId))
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ object Round {
|
|||
import reactivemongo.api.bson._
|
||||
import lila.db.BSON
|
||||
import lila.db.dsl._
|
||||
import scala.util.{ Try, Success, Failure }
|
||||
import BSON.BSONJodaDateTimeHandler
|
||||
|
||||
private implicit val ResultBSONHandler = booleanAnyValHandler[Result](_.win, Result.apply)
|
||||
|
@ -41,17 +42,19 @@ object Round {
|
|||
def encode(puzzleId: PuzzleId) = puzzleId + shiftValue
|
||||
def decode(puzzleId: PuzzleId) = puzzleId - shiftValue
|
||||
|
||||
implicit val roundIdHandler: BSONHandler[BSONString, Id] = new BSONHandler[BSONString, Id] {
|
||||
private val sep = ':'
|
||||
def read(bs: BSONString) = bs.value split sep match {
|
||||
case Array(userId, puzzleId) => Id(userId, decode(Integer parseInt puzzleId))
|
||||
case _ => sys error s"Invalid puzzle round id ${bs.value}"
|
||||
}
|
||||
def write(id: Id) = {
|
||||
private val idSep = ':'
|
||||
implicit val roundIdHandler = tryHandler[Id](
|
||||
{
|
||||
case BSONString(v) => v split idSep match {
|
||||
case Array(userId, puzzleId) => Success(Id(userId, decode(Integer parseInt puzzleId)))
|
||||
case _ => handlerBadValue(s"Invalid puzzle round id $v")
|
||||
}
|
||||
},
|
||||
id => {
|
||||
val puzzleId = "%05d".format(encode(id.puzzleId))
|
||||
BSONString(s"${id.userId}$sep$puzzleId")
|
||||
BSONString(s"${id.userId}$idSep$puzzleId")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
implicit val RoundBSONHandler = new BSON[Round] {
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ private[puzzle] final class Selector(
|
|||
}
|
||||
}
|
||||
}
|
||||
}.mon(_.puzzle.selector.time) flattenWith NoPuzzlesAvailableException addEffect { puzzle =>
|
||||
}.mon(_.puzzle.selector.time) orFailWith NoPuzzlesAvailableException addEffect { puzzle =>
|
||||
if (puzzle.vote.sum < -1000)
|
||||
logger.info(s"Select #${puzzle.id} vote.sum: ${puzzle.vote.sum} for ${me.fold("Anon")(_.username)} (${me.fold("?")(_.perfs.puzzle.intRating.toString)})")
|
||||
else
|
||||
|
|
|
@ -40,6 +40,6 @@ object TreeBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private val logChessError = (id: String) => (err: String) =>
|
||||
logger.warn(s"TreeBuilder https://lichess.org/$id ${err.lines.toList.headOption}")
|
||||
private val logChessError = (id: Game.ID) => (err: String) =>
|
||||
logger.warn(s"TreeBuilder https://lichess.org/$id ${err.linesIterator.toList.headOption}")
|
||||
}
|
||||
|
|
|
@ -29,17 +29,16 @@ final class Env(
|
|||
)(implicit system: ActorSystem, scheduler: Scheduler) {
|
||||
|
||||
private val config = appConfig.get[SecurityConfig]("security")(SecurityConfig.loader)
|
||||
import config._
|
||||
import net.baseUrl
|
||||
import config.net.baseUrl
|
||||
|
||||
// val recaptchaPublicConfig = recaptcha.public
|
||||
|
||||
lazy val firewall = new Firewall(
|
||||
coll = db(collection.firewall),
|
||||
coll = db(config.collection.firewall),
|
||||
scheduler = scheduler
|
||||
)
|
||||
|
||||
lazy val flood = new Flood(config.floodDuration)
|
||||
lazy val flood = wire[Flood]
|
||||
|
||||
lazy val recaptcha: Recaptcha =
|
||||
if (config.recaptchaC.enabled) wire[RecaptchaGoogle]
|
||||
|
@ -51,11 +50,11 @@ final class Env(
|
|||
|
||||
lazy val userSpyApi = wire[UserSpyApi]
|
||||
|
||||
lazy val store = new Store(db(collection.security))
|
||||
lazy val store = new Store(db(config.collection.security))
|
||||
|
||||
lazy val ipIntel = {
|
||||
def mk = (email: EmailAddress) => wire[IpIntel]
|
||||
mk(ipIntelEmail)
|
||||
mk(config.ipIntelEmail)
|
||||
}
|
||||
|
||||
lazy val ugcArmedSetting = settingStore[Boolean](
|
||||
|
@ -64,7 +63,7 @@ final class Env(
|
|||
text = "Enable the user garbage collector".some
|
||||
)
|
||||
|
||||
lazy val printBan = new PrintBan(db(collection.printBan))
|
||||
lazy val printBan = new PrintBan(db(config.collection.printBan))
|
||||
|
||||
lazy val garbageCollector = {
|
||||
def mk: (() => Boolean) => GarbageCollector = isArmed => wire[GarbageCollector]
|
||||
|
@ -84,20 +83,20 @@ final class Env(
|
|||
|
||||
lazy val passwordReset = {
|
||||
def mk = (s: Secret) => wire[PasswordReset]
|
||||
mk(passwordResetSecret)
|
||||
mk(config.passwordResetSecret)
|
||||
}
|
||||
|
||||
lazy val magicLink = {
|
||||
def mk = (s: Secret) => wire[MagicLink]
|
||||
mk(passwordResetSecret)
|
||||
mk(config.passwordResetSecret)
|
||||
}
|
||||
|
||||
lazy val emailChange = {
|
||||
def mk = (s: Secret) => wire[EmailChange]
|
||||
mk(emailChangeSecret)
|
||||
mk(config.emailChangeSecret)
|
||||
}
|
||||
|
||||
lazy val loginToken = new LoginToken(loginTokenSecret, userRepo)
|
||||
lazy val loginToken = new LoginToken(config.loginTokenSecret, userRepo)
|
||||
|
||||
lazy val automaticEmail = wire[AutomaticEmail]
|
||||
|
||||
|
@ -109,7 +108,7 @@ final class Env(
|
|||
|
||||
private lazy val disposableEmailDomain = new DisposableEmailDomain(
|
||||
ws = ws,
|
||||
providerUrl = disposableEmail.providerUrl,
|
||||
providerUrl = config.disposableEmail.providerUrl,
|
||||
checkMailBlocked = () => checkMail.fetchAllBlocked
|
||||
)
|
||||
|
||||
|
@ -124,7 +123,7 @@ final class Env(
|
|||
lazy val spam = new Spam(spamKeywordsSetting.get)
|
||||
|
||||
scheduler.scheduleOnce(30 seconds)(disposableEmailDomain.refresh)
|
||||
scheduler.scheduleWithFixedDelay(disposableEmail.refreshDelay, disposableEmail.refreshDelay) {
|
||||
scheduler.scheduleWithFixedDelay(config.disposableEmail.refreshDelay, config.disposableEmail.refreshDelay) {
|
||||
() => disposableEmailDomain.refresh
|
||||
}
|
||||
|
||||
|
@ -138,7 +137,7 @@ final class Env(
|
|||
|
||||
lazy val api = wire[SecurityApi]
|
||||
|
||||
lazy val csrfRequestHandler = new CSRFRequestHandler(net)
|
||||
lazy val csrfRequestHandler = wire[CSRFRequestHandler]
|
||||
|
||||
def cli = wire[Cli]
|
||||
|
||||
|
|
|
@ -10,22 +10,23 @@ import lila.common.EmailAddress
|
|||
|
||||
import SecurityConfig._
|
||||
|
||||
private case class SecurityConfig(
|
||||
collection: Collection,
|
||||
@ConfigName("flood.duration") floodDuration: FiniteDuration,
|
||||
@ConfigName("geoip") geoIP: GeoIP.Config,
|
||||
@ConfigName("password_reset.secret") passwordResetSecret: Secret,
|
||||
@ConfigName("email_config") emailConfirm: EmailConfirm,
|
||||
@ConfigName("email_change.secret") emailChangeSecret: Secret,
|
||||
@ConfigName("login_token.secret") loginTokenSecret: Secret,
|
||||
tor: Tor,
|
||||
@ConfigName("disposable_email") disposableEmail: DisposableEmail,
|
||||
@ConfigName("dns_api") dnsApi: DnsApi,
|
||||
@ConfigName("check_mail_api") checkMail: CheckMail,
|
||||
recaptchaC: Recaptcha.Config,
|
||||
mailgun: Mailgun.Config,
|
||||
net: NetConfig,
|
||||
@ConfigName("ipintel.email") ipIntelEmail: EmailAddress
|
||||
@Module
|
||||
private class SecurityConfig(
|
||||
val collection: Collection,
|
||||
@ConfigName("flood.duration") val floodDuration: FiniteDuration,
|
||||
@ConfigName("geoip") val geoIP: GeoIP.Config,
|
||||
@ConfigName("password_reset.secret") val passwordResetSecret: Secret,
|
||||
@ConfigName("email_config") val emailConfirm: EmailConfirm,
|
||||
@ConfigName("email_change.secret") val emailChangeSecret: Secret,
|
||||
@ConfigName("login_token.secret") val loginTokenSecret: Secret,
|
||||
val tor: Tor,
|
||||
@ConfigName("disposable_email") val disposableEmail: DisposableEmail,
|
||||
@ConfigName("dns_api") val dnsApi: DnsApi,
|
||||
@ConfigName("check_mail_api") val checkMail: CheckMail,
|
||||
val recaptchaC: Recaptcha.Config,
|
||||
val mailgun: Mailgun.Config,
|
||||
val net: NetConfig,
|
||||
@ConfigName("ipintel.email") val ipIntelEmail: EmailAddress
|
||||
)
|
||||
|
||||
private object SecurityConfig {
|
||||
|
|
|
@ -18,7 +18,7 @@ object PublicLine {
|
|||
|
||||
import reactivemongo.api.bson._
|
||||
import lila.db.dsl._
|
||||
private implicit val SourceHandler = lila.db.BSON.tryHandler[Source](
|
||||
private implicit val SourceHandler = lila.db.dsl.tryHandler[Source](
|
||||
{
|
||||
case BSONString(v) => v split ':' match {
|
||||
case Array("t", id) => Success(Source.Tournament(id))
|
||||
|
@ -38,7 +38,7 @@ object PublicLine {
|
|||
|
||||
private val objectHandler = Macros.handler[PublicLine]
|
||||
|
||||
implicit val PublicLineBSONHandler = lila.db.BSON.tryHandler[PublicLine](
|
||||
implicit val PublicLineBSONHandler = lila.db.dsl.tryHandler[PublicLine](
|
||||
{
|
||||
case doc: BSONDocument => objectHandler readTry doc
|
||||
case BSONString(text) => Success(PublicLine(text, none, none))
|
||||
|
|
|
@ -32,7 +32,7 @@ final class StreamerApi(
|
|||
def findOrInit(user: User): Fu[Option[Streamer.WithUser]] =
|
||||
find(user) orElse {
|
||||
val s = Streamer.WithUser(Streamer make user, user)
|
||||
coll insert s.streamer inject s.some
|
||||
coll.insert.one(s.streamer) inject s.some
|
||||
}
|
||||
|
||||
def withUser(s: Stream): Fu[Option[Streamer.WithUserAndStream]] =
|
||||
|
@ -48,14 +48,14 @@ final class StreamerApi(
|
|||
def setSeenAt(user: User): Funit =
|
||||
listedIdsCache.get flatMap { ids =>
|
||||
ids.contains(Streamer.Id(user.id)) ??
|
||||
coll.update($id(user.id), $set("seenAt" -> DateTime.now)).void
|
||||
coll.update.one($id(user.id), $set("seenAt" -> DateTime.now)).void
|
||||
}
|
||||
|
||||
def setLiveNow(ids: List[Streamer.Id]): Funit =
|
||||
coll.update($doc("_id" $in ids), $set("liveAt" -> DateTime.now), multi = true).void
|
||||
coll.update.one($doc("_id" $in ids), $set("liveAt" -> DateTime.now), multi = true).void
|
||||
|
||||
private[streamer] def mostRecentlySeenIds(ids: List[Streamer.Id], max: Int): Fu[Set[Streamer.Id]] =
|
||||
coll.find($inIds(ids))
|
||||
coll.ext.find($inIds(ids))
|
||||
.sort($doc("seenAt" -> -1))
|
||||
.list[Bdoc](max) map {
|
||||
_ flatMap {
|
||||
|
@ -65,7 +65,7 @@ final class StreamerApi(
|
|||
|
||||
def update(prev: Streamer, data: StreamerForm.UserData, asMod: Boolean): Fu[Streamer.ModChange] = {
|
||||
val streamer = data(prev, asMod)
|
||||
coll.update($id(streamer.id), streamer) >>-
|
||||
coll.update.one($id(streamer.id), streamer) >>-
|
||||
listedIdsCache.refresh inject {
|
||||
val modChange = Streamer.ModChange(
|
||||
list = prev.approval.granted != streamer.approval.granted option streamer.approval.granted,
|
||||
|
@ -88,7 +88,7 @@ final class StreamerApi(
|
|||
}
|
||||
|
||||
def demote(userId: User.ID): Funit =
|
||||
coll.update(
|
||||
coll.update.one(
|
||||
$id(userId),
|
||||
$set(
|
||||
"approval.requested" -> false,
|
||||
|
@ -99,22 +99,22 @@ final class StreamerApi(
|
|||
|
||||
def create(u: User): Funit =
|
||||
isStreamer(u) flatMap { exists =>
|
||||
!exists ?? coll.insert(Streamer make u).void
|
||||
!exists ?? coll.insert.one(Streamer make u).void
|
||||
}
|
||||
|
||||
def isStreamer(user: User): Fu[Boolean] = listedIdsCache.get.dmap(_ contains Streamer.Id(user.id))
|
||||
|
||||
def uploadPicture(s: Streamer, picture: Photographer.Uploaded): Funit =
|
||||
photographer(s.id.value, picture).flatMap { pic =>
|
||||
coll.update($id(s.id), $set("picturePath" -> pic.path)).void
|
||||
coll.update.one($id(s.id), $set("picturePath" -> pic.path)).void
|
||||
}
|
||||
|
||||
def deletePicture(s: Streamer): Funit =
|
||||
coll.update($id(s.id), $unset("picturePath")).void
|
||||
coll.update.one($id(s.id), $unset("picturePath")).void
|
||||
|
||||
// unapprove after a week if you never streamed
|
||||
def autoDemoteFakes: Funit =
|
||||
coll.update(
|
||||
coll.update.one(
|
||||
$doc(
|
||||
"liveAt" $exists false,
|
||||
"approval.granted" -> true,
|
||||
|
|
|
@ -78,7 +78,7 @@ object Authenticator {
|
|||
F.sha512 -> true
|
||||
)
|
||||
|
||||
private[user] implicit val HashedPasswordBsonHandler = lila.db.BSON.quickHandler[HashedPassword](
|
||||
private[user] implicit val HashedPasswordBsonHandler = quickHandler[HashedPassword](
|
||||
{ case v: BSONBinary => HashedPassword(v.byteArray) },
|
||||
v => BSONBinary(v.bytes, Subtype.GenericBinarySubtype)
|
||||
)
|
||||
|
|
|
@ -56,7 +56,7 @@ object TotpSecret {
|
|||
TotpSecret(secret)
|
||||
}
|
||||
|
||||
private[user] val totpSecretBSONHandler = lila.db.BSON.quickHandler[TotpSecret](
|
||||
private[user] val totpSecretBSONHandler = lila.db.dsl.quickHandler[TotpSecret](
|
||||
{ case v: BSONBinary => TotpSecret(v.byteArray) },
|
||||
v => BSONBinary(v.secret, Subtype.GenericBinarySubtype)
|
||||
)
|
||||
|
|
|
@ -8,7 +8,8 @@ import reactivemongo.api.bson._
|
|||
import scala.concurrent.duration._
|
||||
|
||||
final class TrophyApi(
|
||||
coll: Coll, kindColl: Coll
|
||||
coll: Coll,
|
||||
kindColl: Coll
|
||||
)(implicit system: akka.actor.ActorSystem) {
|
||||
|
||||
private val trophyKindObjectBSONHandler = Macros.handler[TrophyKind]
|
||||
|
@ -22,10 +23,8 @@ final class TrophyApi(
|
|||
logger = logger
|
||||
)
|
||||
|
||||
private implicit val trophyKindStringBSONHandler = lila.db.BSON.quickHandler[TrophyKind](
|
||||
{ case BSONString(str) => kindCache sync str },
|
||||
x => BSONString(x._id)
|
||||
)
|
||||
private implicit val trophyKindStringBSONHandler =
|
||||
BSONStringHandler.as[TrophyKind](kindCache.sync, _._id)
|
||||
|
||||
private implicit val trophyBSONHandler = Macros.handler[Trophy]
|
||||
|
||||
|
|
Loading…
Reference in New Issue