implement resource link checker
parent
ae9d53d472
commit
4aec266719
|
@ -7,7 +7,9 @@ import play.api.{ Configuration, Mode }
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
import lila.common.config._
|
import lila.common.config._
|
||||||
|
import lila.common.Bus
|
||||||
import lila.user.User
|
import lila.user.User
|
||||||
|
import lila.chat.GetLinkCheck
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
final class Env(
|
final class Env(
|
||||||
|
@ -55,6 +57,7 @@ final class Env(
|
||||||
|
|
||||||
val config = ApiConfig loadFrom appConfig
|
val config = ApiConfig loadFrom appConfig
|
||||||
import config.apiToken
|
import config.apiToken
|
||||||
|
import net.domain
|
||||||
|
|
||||||
lazy val pgnDump: PgnDump = wire[PgnDump]
|
lazy val pgnDump: PgnDump = wire[PgnDump]
|
||||||
|
|
||||||
|
@ -85,6 +88,12 @@ final class Env(
|
||||||
)
|
)
|
||||||
if (mode == Mode.Prod) system.scheduler.scheduleOnce(5 seconds)(influxEvent.start())
|
if (mode == Mode.Prod) system.scheduler.scheduleOnce(5 seconds)(influxEvent.start())
|
||||||
|
|
||||||
|
private lazy val linkCheck = wire[LinkCheck]
|
||||||
|
|
||||||
|
Bus.subscribeFun("chatLinkCheck") {
|
||||||
|
case GetLinkCheck(line, source, promise) => promise completeWith linkCheck(line, source)
|
||||||
|
}
|
||||||
|
|
||||||
system.scheduler.scheduleWithFixedDelay(1 minute, 1 minute) { () =>
|
system.scheduler.scheduleWithFixedDelay(1 minute, 1 minute) { () =>
|
||||||
lila.mon.bus.classifiers.update(lila.common.Bus.size)
|
lila.mon.bus.classifiers.update(lila.common.Bus.size)
|
||||||
// ensure the Lichess user is online
|
// ensure the Lichess user is online
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
package lila.api
|
||||||
|
|
||||||
|
import cats.implicits._
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
|
||||||
|
import lila.chat.UserLine
|
||||||
|
import lila.common.config.NetDomain
|
||||||
|
import lila.hub.actorApi.shutup.PublicSource
|
||||||
|
import lila.simul.Simul
|
||||||
|
import lila.simul.SimulApi
|
||||||
|
import lila.swiss.Swiss
|
||||||
|
import lila.swiss.SwissApi
|
||||||
|
import lila.team.Team
|
||||||
|
import lila.team.TeamRepo
|
||||||
|
import lila.tournament.Tournament
|
||||||
|
import lila.tournament.TournamentRepo
|
||||||
|
import lila.user.User
|
||||||
|
import lila.study.Study
|
||||||
|
import lila.study.StudyRepo
|
||||||
|
|
||||||
|
/* Determine if a link to a lichess resource
|
||||||
|
* can be posted from another lichess resource.
|
||||||
|
* Owners of a resource can post any link on it (but not to it).
|
||||||
|
* Links to a team resource can be posted from another resource of the same team.
|
||||||
|
* Links to official resources can be posted from anywhere.
|
||||||
|
* */
|
||||||
|
final private class LinkCheck(
|
||||||
|
domain: NetDomain,
|
||||||
|
teamRepo: TeamRepo,
|
||||||
|
tournamentRepo: TournamentRepo,
|
||||||
|
simulApi: SimulApi,
|
||||||
|
swissApi: SwissApi,
|
||||||
|
studyRepo: StudyRepo
|
||||||
|
)(implicit ec: ExecutionContext) {
|
||||||
|
|
||||||
|
import LinkCheck._
|
||||||
|
|
||||||
|
def apply(line: UserLine, source: PublicSource): Fu[Boolean] = {
|
||||||
|
if (multipleLinks find line.text) fuFalse
|
||||||
|
else
|
||||||
|
line.text match {
|
||||||
|
case tournamentLinkR(id) => withSource(source, tourLink)(id, line)
|
||||||
|
case simulLinkR(id) => withSource(source, simulLink)(id, line)
|
||||||
|
case swissLinkR(id) => withSource(source, swissLink)(id, line)
|
||||||
|
case studyLinkR(id) => withSource(source, studyLink)(id, line)
|
||||||
|
case _ => fuTrue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def withSource(
|
||||||
|
source: PublicSource,
|
||||||
|
f: (String, FullSource) => Fu[Boolean]
|
||||||
|
)(id: String, line: UserLine): Fu[Boolean] = {
|
||||||
|
source match {
|
||||||
|
case PublicSource.Tournament(id) => tournamentRepo byId id map2 FullSource.TournamentSource
|
||||||
|
case PublicSource.Simul(id) => simulApi find id map2 FullSource.SimulSource
|
||||||
|
case PublicSource.Swiss(id) => swissApi byId Swiss.Id(id) map2 FullSource.SwissSource
|
||||||
|
case PublicSource.Team(id) => teamRepo byId id map2 FullSource.TeamSource
|
||||||
|
case PublicSource.Study(id) => studyRepo byId Study.Id(id) map2 FullSource.StudySource
|
||||||
|
case _ => fuccess(none)
|
||||||
|
}
|
||||||
|
} flatMap {
|
||||||
|
_ ?? { source =>
|
||||||
|
// the owners of a chat can post whichever link they like
|
||||||
|
if (source.owners(line.userId)) fuTrue
|
||||||
|
else f(id, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def tourLink(tourId: Tournament.ID, source: FullSource) =
|
||||||
|
tournamentRepo byId tourId flatMap {
|
||||||
|
_ ?? { tour =>
|
||||||
|
fuccess(tour.isScheduled) >>| {
|
||||||
|
source.teamId ?? { sourceTeamId =>
|
||||||
|
fuccess(tour.conditions.teamMember.exists(_.teamId == sourceTeamId)) >>|
|
||||||
|
tournamentRepo.isForTeam(tour.id, sourceTeamId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def simulLink(simulId: Tournament.ID, source: FullSource) =
|
||||||
|
simulApi teamOf simulId map {
|
||||||
|
_ exists source.teamId.has
|
||||||
|
}
|
||||||
|
|
||||||
|
private def swissLink(swissId: String, source: FullSource) =
|
||||||
|
swissApi teamOf Swiss.Id(swissId) map {
|
||||||
|
_ exists source.teamId.has
|
||||||
|
}
|
||||||
|
|
||||||
|
private def studyLink(studyId: String, source: FullSource) = fuFalse
|
||||||
|
|
||||||
|
private val multipleLinks = s"$domain.+$domain".r.unanchored
|
||||||
|
private val tournamentLinkR = s"$domain/tournament/(\\w+)".r.unanchored
|
||||||
|
private val simulLinkR = s"$domain/simul/(\\w+)".r.unanchored
|
||||||
|
private val swissLinkR = s"$domain/swiss/(\\w+)".r.unanchored
|
||||||
|
private val studyLinkR = s"$domain/study/(\\w+)".r.unanchored
|
||||||
|
}
|
||||||
|
|
||||||
|
private object LinkCheck {
|
||||||
|
|
||||||
|
sealed trait FullSource {
|
||||||
|
def owners: Set[User.ID]
|
||||||
|
def teamId: Option[Team.ID]
|
||||||
|
}
|
||||||
|
|
||||||
|
object FullSource {
|
||||||
|
case class TournamentSource(value: Tournament) extends FullSource {
|
||||||
|
def owners = Set(value.createdBy)
|
||||||
|
def teamId = value.conditions.teamMember.map(_.teamId)
|
||||||
|
}
|
||||||
|
case class SimulSource(value: Simul) extends FullSource {
|
||||||
|
def owners = Set(value.hostId)
|
||||||
|
def teamId = value.team
|
||||||
|
}
|
||||||
|
case class SwissSource(value: Swiss) extends FullSource {
|
||||||
|
def owners = Set(value.createdBy)
|
||||||
|
def teamId = value.teamId.some
|
||||||
|
}
|
||||||
|
case class TeamSource(value: Team) extends FullSource {
|
||||||
|
def owners = value.leaders
|
||||||
|
def teamId = value.id.some
|
||||||
|
}
|
||||||
|
case class StudySource(value: Study) extends FullSource {
|
||||||
|
def owners = value.members.idSet
|
||||||
|
def teamId = none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,9 +4,9 @@ import chess.Color
|
||||||
import reactivemongo.api.ReadPreference
|
import reactivemongo.api.ReadPreference
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
import lila.common.Bus
|
||||||
import lila.common.config.NetDomain
|
import lila.common.config.NetDomain
|
||||||
import lila.common.String.noShouting
|
import lila.common.String.noShouting
|
||||||
import lila.common.Bus
|
|
||||||
import lila.db.dsl._
|
import lila.db.dsl._
|
||||||
import lila.hub.actorApi.shutup.{ PublicSource, RecordPrivateChat, RecordPublicChat }
|
import lila.hub.actorApi.shutup.{ PublicSource, RecordPrivateChat, RecordPublicChat }
|
||||||
import lila.memo.CacheApi._
|
import lila.memo.CacheApi._
|
||||||
|
@ -23,7 +23,7 @@ final class ChatApi(
|
||||||
cacheApi: lila.memo.CacheApi,
|
cacheApi: lila.memo.CacheApi,
|
||||||
maxLinesPerChat: Chat.MaxLines,
|
maxLinesPerChat: Chat.MaxLines,
|
||||||
netDomain: NetDomain
|
netDomain: NetDomain
|
||||||
)(implicit ec: scala.concurrent.ExecutionContext) {
|
)(implicit ec: scala.concurrent.ExecutionContext, actorSystem: akka.actor.ActorSystem) {
|
||||||
|
|
||||||
import Chat.{ chatIdBSONHandler, userChatBSONHandler }
|
import Chat.{ chatIdBSONHandler, userChatBSONHandler }
|
||||||
|
|
||||||
|
@ -88,20 +88,31 @@ final class ChatApi(
|
||||||
): Funit =
|
): Funit =
|
||||||
makeLine(chatId, userId, text) flatMap {
|
makeLine(chatId, userId, text) flatMap {
|
||||||
_ ?? { line =>
|
_ ?? { line =>
|
||||||
pushLine(chatId, line) >>- {
|
linkCheck(line, publicSource) flatMap {
|
||||||
if (publicSource.isDefined) cached invalidate chatId
|
case false =>
|
||||||
shutup ! {
|
logger.info(s"Link check rejected $line in $publicSource")
|
||||||
publicSource match {
|
funit
|
||||||
case Some(source) => RecordPublicChat(userId, text, source)
|
case true =>
|
||||||
case _ => RecordPrivateChat(chatId.value, userId, text)
|
pushLine(chatId, line) >>- {
|
||||||
|
if (publicSource.isDefined) cached invalidate chatId
|
||||||
|
shutup ! {
|
||||||
|
publicSource match {
|
||||||
|
case Some(source) => RecordPublicChat(userId, text, source)
|
||||||
|
case _ => RecordPrivateChat(chatId.value, userId, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
publish(chatId, actorApi.ChatLine(chatId, line), busChan)
|
||||||
|
lila.mon.chat.message(publicSource.fold("player")(_.parentName), line.troll).increment()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
publish(chatId, actorApi.ChatLine(chatId, line), busChan)
|
|
||||||
lila.mon.chat.message(publicSource.fold("player")(_.parentName), line.troll).increment()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def linkCheck(line: UserLine, source: Option[PublicSource]) =
|
||||||
|
source.fold(fuccess(true)) { s =>
|
||||||
|
Bus.ask[Boolean]("chatLinkCheck") { GetLinkCheck(line, s, _) }
|
||||||
|
}
|
||||||
|
|
||||||
def clear(chatId: Chat.Id) = coll.delete.one($id(chatId)).void
|
def clear(chatId: Chat.Id) = coll.delete.one($id(chatId)).void
|
||||||
|
|
||||||
def system(chatId: Chat.Id, text: String, busChan: BusChan.Select): Funit = {
|
def system(chatId: Chat.Id, text: String, busChan: BusChan.Select): Funit = {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package lila.chat
|
package lila.chat
|
||||||
|
|
||||||
import lila.user.User
|
import lila.user.User
|
||||||
|
import scala.concurrent.Promise
|
||||||
|
import lila.hub.actorApi.shutup.PublicSource
|
||||||
|
|
||||||
case class UserModInfo(
|
case class UserModInfo(
|
||||||
user: User,
|
user: User,
|
||||||
|
@ -21,3 +23,5 @@ object BusChan {
|
||||||
|
|
||||||
type Select = BusChan.type => BusChan
|
type Select = BusChan.type => BusChan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case class GetLinkCheck(line: UserLine, source: PublicSource, promise: Promise[Boolean])
|
||||||
|
|
|
@ -67,7 +67,7 @@ object HTTPRequest {
|
||||||
case class UaMatcher(rStr: String) {
|
case class UaMatcher(rStr: String) {
|
||||||
private val regex = rStr.r
|
private val regex = rStr.r
|
||||||
|
|
||||||
def apply(req: RequestHeader): Boolean = userAgent(req) ?? { regex.find(_) }
|
def apply(req: RequestHeader): Boolean = userAgent(req) ?? regex.find
|
||||||
}
|
}
|
||||||
|
|
||||||
def isFishnet(req: RequestHeader) = req.path startsWith "/fishnet/"
|
def isFishnet(req: RequestHeader) = req.path startsWith "/fishnet/"
|
||||||
|
|
|
@ -9,7 +9,7 @@ import lila.common.config.NetDomain
|
||||||
final class PromotionApi(domain: NetDomain) {
|
final class PromotionApi(domain: NetDomain) {
|
||||||
|
|
||||||
def test(user: User)(text: String): Boolean =
|
def test(user: User)(text: String): Boolean =
|
||||||
user.isVerified || {
|
user.isVerified || user.isAdmin || {
|
||||||
val promotions = extract(text)
|
val promotions = extract(text)
|
||||||
promotions.isEmpty || {
|
promotions.isEmpty || {
|
||||||
val prev = ~cache.getIfPresent(user.id)
|
val prev = ~cache.getIfPresent(user.id)
|
||||||
|
@ -35,6 +35,8 @@ final class PromotionApi(domain: NetDomain) {
|
||||||
s"$domain/team/([\\w-]+)",
|
s"$domain/team/([\\w-]+)",
|
||||||
s"$domain/tournament/(\\w+)",
|
s"$domain/tournament/(\\w+)",
|
||||||
s"$domain/swiss/(\\w+)",
|
s"$domain/swiss/(\\w+)",
|
||||||
|
s"$domain/simul/(\\w+)",
|
||||||
|
s"$domain/study/(\\w+)",
|
||||||
"""(?:youtube\.com|youtu\.be)/(?:watch)?(?:\?v=)?([^"&?/ ]{11})""",
|
"""(?:youtube\.com|youtu\.be)/(?:watch)?(?:\?v=)?([^"&?/ ]{11})""",
|
||||||
"""youtube\.com/channel/([\w-]{24})""",
|
"""youtube\.com/channel/([\w-]{24})""",
|
||||||
"""twitch\.tv/([a-zA-Z0-9](?:\w{2,24}+))"""
|
"""twitch\.tv/([a-zA-Z0-9](?:\w{2,24}+))"""
|
||||||
|
|
|
@ -17,7 +17,7 @@ case class Simul(
|
||||||
variants: List[Variant],
|
variants: List[Variant],
|
||||||
position: Option[StartingPosition],
|
position: Option[StartingPosition],
|
||||||
createdAt: DateTime,
|
createdAt: DateTime,
|
||||||
hostId: String,
|
hostId: User.ID,
|
||||||
hostRating: Int,
|
hostRating: Int,
|
||||||
hostGameId: Option[String], // game the host is focusing on
|
hostGameId: Option[String], // game the host is focusing on
|
||||||
startedAt: Option[DateTime],
|
startedAt: Option[DateTime],
|
||||||
|
|
|
@ -6,8 +6,10 @@ import play.api.libs.json.Json
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
import lila.common.{ Bus, Debouncer }
|
import lila.common.{ Bus, Debouncer }
|
||||||
|
import lila.db.dsl._
|
||||||
import lila.game.{ Game, GameRepo, PerfPicker }
|
import lila.game.{ Game, GameRepo, PerfPicker }
|
||||||
import lila.hub.actorApi.timeline.{ Propagate, SimulCreate, SimulJoin }
|
import lila.hub.actorApi.timeline.{ Propagate, SimulCreate, SimulJoin }
|
||||||
|
import lila.hub.LightTeam.TeamID
|
||||||
import lila.memo.CacheApi._
|
import lila.memo.CacheApi._
|
||||||
import lila.socket.Socket.SendToFlag
|
import lila.socket.Socket.SendToFlag
|
||||||
import lila.user.{ User, UserRepo }
|
import lila.user.{ User, UserRepo }
|
||||||
|
@ -222,6 +224,9 @@ final class SimulApi(
|
||||||
def idToName(id: Simul.ID): Fu[Option[String]] =
|
def idToName(id: Simul.ID): Fu[Option[String]] =
|
||||||
repo find id dmap2 { _.fullName }
|
repo find id dmap2 { _.fullName }
|
||||||
|
|
||||||
|
def teamOf(id: Simul.ID): Fu[Option[TeamID]] =
|
||||||
|
repo.coll.primitiveOne[TeamID]($id(id), "team")
|
||||||
|
|
||||||
private def makeGame(simul: Simul, host: User)(
|
private def makeGame(simul: Simul, host: User)(
|
||||||
pairingAndNumber: (SimulPairing, Int)
|
pairingAndNumber: (SimulPairing, Int)
|
||||||
): Fu[(Game, chess.Color)] =
|
): Fu[(Game, chess.Color)] =
|
||||||
|
|
|
@ -10,7 +10,7 @@ import lila.db.BSON.BSONJodaDateTimeHandler
|
||||||
import lila.db.dsl._
|
import lila.db.dsl._
|
||||||
import lila.user.User
|
import lila.user.User
|
||||||
|
|
||||||
final private[simul] class SimulRepo(simulColl: Coll)(implicit ec: scala.concurrent.ExecutionContext) {
|
final private[simul] class SimulRepo(val coll: Coll)(implicit ec: scala.concurrent.ExecutionContext) {
|
||||||
|
|
||||||
implicit private val SimulStatusBSONHandler = tryHandler[SimulStatus](
|
implicit private val SimulStatusBSONHandler = tryHandler[SimulStatus](
|
||||||
{ case BSONInteger(v) => SimulStatus(v) toTry s"No such simul status: $v" },
|
{ case BSONInteger(v) => SimulStatus(v) toTry s"No such simul status: $v" },
|
||||||
|
@ -57,13 +57,13 @@ final private[simul] class SimulRepo(simulColl: Coll)(implicit ec: scala.concurr
|
||||||
private val createdSort = $sort desc "createdAt"
|
private val createdSort = $sort desc "createdAt"
|
||||||
|
|
||||||
def find(id: Simul.ID): Fu[Option[Simul]] =
|
def find(id: Simul.ID): Fu[Option[Simul]] =
|
||||||
simulColl.byId[Simul](id)
|
coll.byId[Simul](id)
|
||||||
|
|
||||||
def byIds(ids: List[Simul.ID]): Fu[List[Simul]] =
|
def byIds(ids: List[Simul.ID]): Fu[List[Simul]] =
|
||||||
simulColl.byIds[Simul](ids)
|
coll.byIds[Simul](ids)
|
||||||
|
|
||||||
def exists(id: Simul.ID): Fu[Boolean] =
|
def exists(id: Simul.ID): Fu[Boolean] =
|
||||||
simulColl.exists($id(id))
|
coll.exists($id(id))
|
||||||
|
|
||||||
def findStarted(id: Simul.ID): Fu[Option[Simul]] =
|
def findStarted(id: Simul.ID): Fu[Option[Simul]] =
|
||||||
find(id) map (_ filter (_.isStarted))
|
find(id) map (_ filter (_.isStarted))
|
||||||
|
@ -72,22 +72,22 @@ final private[simul] class SimulRepo(simulColl: Coll)(implicit ec: scala.concurr
|
||||||
find(id) map (_ filter (_.isCreated))
|
find(id) map (_ filter (_.isCreated))
|
||||||
|
|
||||||
def findPending(hostId: User.ID): Fu[List[Simul]] =
|
def findPending(hostId: User.ID): Fu[List[Simul]] =
|
||||||
simulColl.list[Simul](createdSelect ++ $doc("hostId" -> hostId))
|
coll.list[Simul](createdSelect ++ $doc("hostId" -> hostId))
|
||||||
|
|
||||||
def byTeamLeaders(teamId: String, hostIds: Seq[User.ID]): Fu[List[Simul]] =
|
def byTeamLeaders(teamId: String, hostIds: Seq[User.ID]): Fu[List[Simul]] =
|
||||||
simulColl
|
coll
|
||||||
.find(
|
.find(
|
||||||
createdSelect ++
|
createdSelect ++
|
||||||
$doc("hostId" $in hostIds, "team" $in List(BSONString(teamId)))
|
$doc("hostId" $in hostIds, "team" $in List(BSONString(teamId)))
|
||||||
)
|
)
|
||||||
.hint(simulColl hint $doc("hostId" -> 1))
|
.hint(coll hint $doc("hostId" -> 1))
|
||||||
.cursor[Simul]()
|
.cursor[Simul]()
|
||||||
.list()
|
.list()
|
||||||
|
|
||||||
private val featurableSelect = $doc("featurable" -> true)
|
private val featurableSelect = $doc("featurable" -> true)
|
||||||
|
|
||||||
def allCreatedFeaturable: Fu[List[Simul]] =
|
def allCreatedFeaturable: Fu[List[Simul]] =
|
||||||
simulColl
|
coll
|
||||||
.find(
|
.find(
|
||||||
// hits partial index hostSeenAt_-1
|
// hits partial index hostSeenAt_-1
|
||||||
createdSelect ++ featurableSelect ++ $doc(
|
createdSelect ++ featurableSelect ++ $doc(
|
||||||
|
@ -96,7 +96,7 @@ final private[simul] class SimulRepo(simulColl: Coll)(implicit ec: scala.concurr
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.sort(createdSort)
|
.sort(createdSort)
|
||||||
.hint(simulColl hint $doc("hostSeenAt" -> -1))
|
.hint(coll hint $doc("hostSeenAt" -> -1))
|
||||||
.cursor[Simul]()
|
.cursor[Simul]()
|
||||||
.list() map {
|
.list() map {
|
||||||
_.foldLeft(List.empty[Simul]) {
|
_.foldLeft(List.empty[Simul]) {
|
||||||
|
@ -106,29 +106,29 @@ final private[simul] class SimulRepo(simulColl: Coll)(implicit ec: scala.concurr
|
||||||
}
|
}
|
||||||
|
|
||||||
def allStarted: Fu[List[Simul]] =
|
def allStarted: Fu[List[Simul]] =
|
||||||
simulColl
|
coll
|
||||||
.find(startedSelect)
|
.find(startedSelect)
|
||||||
.sort(createdSort)
|
.sort(createdSort)
|
||||||
.cursor[Simul]()
|
.cursor[Simul]()
|
||||||
.list()
|
.list()
|
||||||
|
|
||||||
def allFinishedFeaturable(max: Int): Fu[List[Simul]] =
|
def allFinishedFeaturable(max: Int): Fu[List[Simul]] =
|
||||||
simulColl
|
coll
|
||||||
.find(finishedSelect ++ featurableSelect)
|
.find(finishedSelect ++ featurableSelect)
|
||||||
.sort($sort desc "finishedAt")
|
.sort($sort desc "finishedAt")
|
||||||
.cursor[Simul]()
|
.cursor[Simul]()
|
||||||
.list(max)
|
.list(max)
|
||||||
|
|
||||||
def allNotFinished =
|
def allNotFinished =
|
||||||
simulColl.list[Simul]($doc("status" $ne SimulStatus.Finished.id))
|
coll.list[Simul]($doc("status" $ne SimulStatus.Finished.id))
|
||||||
|
|
||||||
def create(simul: Simul, featurable: Boolean): Funit =
|
def create(simul: Simul, featurable: Boolean): Funit =
|
||||||
simulColl.insert one {
|
coll.insert one {
|
||||||
SimulBSONHandler.writeTry(simul).get ++ featurable.??(featurableSelect)
|
SimulBSONHandler.writeTry(simul).get ++ featurable.??(featurableSelect)
|
||||||
} void
|
} void
|
||||||
|
|
||||||
def update(simul: Simul, featurable: Option[Boolean]) =
|
def update(simul: Simul, featurable: Option[Boolean]) =
|
||||||
simulColl.update
|
coll.update
|
||||||
.one(
|
.one(
|
||||||
$id(simul.id),
|
$id(simul.id),
|
||||||
$set(SimulBSONHandler writeTry simul get) ++ featurable.?? { feat =>
|
$set(SimulBSONHandler writeTry simul get) ++ featurable.?? { feat =>
|
||||||
|
@ -138,10 +138,10 @@ final private[simul] class SimulRepo(simulColl: Coll)(implicit ec: scala.concurr
|
||||||
.void
|
.void
|
||||||
|
|
||||||
def remove(simul: Simul) =
|
def remove(simul: Simul) =
|
||||||
simulColl.delete.one($id(simul.id)).void
|
coll.delete.one($id(simul.id)).void
|
||||||
|
|
||||||
def setHostGameId(simul: Simul, gameId: String) =
|
def setHostGameId(simul: Simul, gameId: String) =
|
||||||
simulColl.update
|
coll.update
|
||||||
.one(
|
.one(
|
||||||
$id(simul.id),
|
$id(simul.id),
|
||||||
$set("hostGameId" -> gameId)
|
$set("hostGameId" -> gameId)
|
||||||
|
@ -149,7 +149,7 @@ final private[simul] class SimulRepo(simulColl: Coll)(implicit ec: scala.concurr
|
||||||
.void
|
.void
|
||||||
|
|
||||||
def setHostSeenNow(simul: Simul) =
|
def setHostSeenNow(simul: Simul) =
|
||||||
simulColl.update
|
coll.update
|
||||||
.one(
|
.one(
|
||||||
$id(simul.id),
|
$id(simul.id),
|
||||||
$set("hostSeenAt" -> DateTime.now)
|
$set("hostSeenAt" -> DateTime.now)
|
||||||
|
@ -157,7 +157,7 @@ final private[simul] class SimulRepo(simulColl: Coll)(implicit ec: scala.concurr
|
||||||
.void
|
.void
|
||||||
|
|
||||||
def setText(simul: Simul, text: String) =
|
def setText(simul: Simul, text: String) =
|
||||||
simulColl.update
|
coll.update
|
||||||
.one(
|
.one(
|
||||||
$id(simul.id),
|
$id(simul.id),
|
||||||
$set("text" -> text)
|
$set("text" -> text)
|
||||||
|
@ -165,7 +165,7 @@ final private[simul] class SimulRepo(simulColl: Coll)(implicit ec: scala.concurr
|
||||||
.void
|
.void
|
||||||
|
|
||||||
def cleanup =
|
def cleanup =
|
||||||
simulColl.delete.one(
|
coll.delete.one(
|
||||||
createdSelect ++ $doc(
|
createdSelect ++ $doc(
|
||||||
"createdAt" -> $doc("$lt" -> (DateTime.now minusMinutes 60))
|
"createdAt" -> $doc("$lt" -> (DateTime.now minusMinutes 60))
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,7 +32,8 @@ case class StudyMembers(members: StudyMember.MemberMap) {
|
||||||
|
|
||||||
def get = members.get _
|
def get = members.get _
|
||||||
|
|
||||||
def ids = members.keys
|
def ids = members.keys
|
||||||
|
def idSet = members.keySet
|
||||||
|
|
||||||
def contributorIds: Set[User.ID] =
|
def contributorIds: Set[User.ID] =
|
||||||
members.view.collect {
|
members.view.collect {
|
||||||
|
|
|
@ -452,6 +452,9 @@ final class SwissApi(
|
||||||
.sort($sort desc "startsAt")
|
.sort($sort desc "startsAt")
|
||||||
.cursor[Swiss]()
|
.cursor[Swiss]()
|
||||||
|
|
||||||
|
def teamOf(id: Swiss.Id): Fu[Option[TeamID]] =
|
||||||
|
colls.swiss.primitiveOne[TeamID]($id(id), "teamId")
|
||||||
|
|
||||||
private def recomputeAndUpdateAll(id: Swiss.Id): Funit =
|
private def recomputeAndUpdateAll(id: Swiss.Id): Funit =
|
||||||
scoring(id).flatMap {
|
scoring(id).flatMap {
|
||||||
_ ?? { res =>
|
_ ?? { res =>
|
||||||
|
|
|
@ -30,7 +30,7 @@ final class TeamApi(
|
||||||
|
|
||||||
import BSONHandlers._
|
import BSONHandlers._
|
||||||
|
|
||||||
def team(id: Team.ID) = teamRepo.coll.byId[Team](id)
|
def team(id: Team.ID) = teamRepo byId id
|
||||||
|
|
||||||
def leaderTeam(id: Team.ID) = teamRepo.coll.byId[LeaderTeam](id, $doc("name" -> true))
|
def leaderTeam(id: Team.ID) = teamRepo.coll.byId[LeaderTeam](id, $doc("name" -> true))
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@ final class TeamRepo(val coll: Coll)(implicit ec: scala.concurrent.ExecutionCont
|
||||||
|
|
||||||
private val lightProjection = $doc("name" -> true).some
|
private val lightProjection = $doc("name" -> true).some
|
||||||
|
|
||||||
|
def byId(id: Team.ID) = coll.byId[Team](id)
|
||||||
|
|
||||||
def byOrderedIds(ids: Seq[Team.ID]) = coll.byOrderedIds[Team, Team.ID](ids)(_.id)
|
def byOrderedIds(ids: Seq[Team.ID]) = coll.byOrderedIds[Team, Team.ID](ids)(_.id)
|
||||||
|
|
||||||
def byLeader(id: Team.ID, leaderId: User.ID): Fu[Option[Team]] =
|
def byLeader(id: Team.ID, leaderId: User.ID): Fu[Option[Team]] =
|
||||||
|
|
|
@ -144,6 +144,9 @@ final class TournamentRepo(val coll: Coll, playerCollName: CollName)(implicit
|
||||||
private[tournament] def setForTeam(tourId: Tournament.ID, teamId: TeamID) =
|
private[tournament] def setForTeam(tourId: Tournament.ID, teamId: TeamID) =
|
||||||
coll.update.one($id(tourId), $addToSet("forTeams" -> teamId))
|
coll.update.one($id(tourId), $addToSet("forTeams" -> teamId))
|
||||||
|
|
||||||
|
def isForTeam(tourId: Tournament.ID, teamId: TeamID) =
|
||||||
|
coll.exists($id(tourId) ++ $doc("forTeams" -> teamId))
|
||||||
|
|
||||||
private[tournament] def withdrawableIds(
|
private[tournament] def withdrawableIds(
|
||||||
userId: User.ID,
|
userId: User.ID,
|
||||||
teamId: Option[TeamID] = None
|
teamId: Option[TeamID] = None
|
||||||
|
|
|
@ -119,8 +119,10 @@ case class User(
|
||||||
|
|
||||||
def addRole(role: String) = copy(roles = role :: roles)
|
def addRole(role: String) = copy(roles = role :: roles)
|
||||||
|
|
||||||
def isVerified = roles.exists(_ contains "ROLE_VERIFIED")
|
def isVerified = roles.exists(_ contains "ROLE_VERIFIED")
|
||||||
def isApiHog = roles.exists(_ contains "ROLE_API_HOG")
|
def isSuperAdmin = roles.exists(_ contains "ROLE_SUPER_ADMIN")
|
||||||
|
def isAdmin = roles.exists(_ contains "ROLE_ADMIN") || isSuperAdmin
|
||||||
|
def isApiHog = roles.exists(_ contains "ROLE_API_HOG")
|
||||||
}
|
}
|
||||||
|
|
||||||
object User {
|
object User {
|
||||||
|
|
Loading…
Reference in New Issue