migration WIP
parent
b0d798fd37
commit
20b372d5ec
|
@ -171,7 +171,7 @@ final class EnvBoot(
|
|||
lazy val common: lila.common.Env = wire[lila.common.Env]
|
||||
lazy val baseUrl = common.netConfig.baseUrl
|
||||
lazy val memo: lila.memo.Env = wire[lila.memo.Env]
|
||||
lazy val db: lila.db.Env = lila.db.Env.main(config)
|
||||
lazy val db: lila.db.Env = lila.db.Env.main(config, lifecycle)
|
||||
lazy val user: lila.user.Env = wire[lila.user.Env]
|
||||
lazy val security: lila.security.Env = wire[lila.security.Env]
|
||||
lazy val hub: lila.hub.Env = wire[lila.hub.Env]
|
||||
|
|
|
@ -12,8 +12,8 @@ import views._
|
|||
|
||||
final class Analyse(
|
||||
env: Env,
|
||||
gameC: Game,
|
||||
roundC: Round
|
||||
gameC: => Game,
|
||||
roundC: => Round
|
||||
) extends LilaController(env) {
|
||||
|
||||
def requestAnalysis(id: String) = Auth { implicit ctx => me =>
|
||||
|
|
|
@ -14,9 +14,11 @@ import lila.common.{ HTTPRequest, IpAddress }
|
|||
|
||||
final class Api(
|
||||
env: Env,
|
||||
gameC: Game
|
||||
gameC: => Game
|
||||
) extends LilaController(env) {
|
||||
|
||||
import Api._
|
||||
|
||||
private val userApi = env.api.userApi
|
||||
private val gameApi = env.api.gameApi
|
||||
|
||||
|
@ -319,6 +321,39 @@ final class Api(
|
|||
}
|
||||
}
|
||||
|
||||
def CookieBasedApiRequest(js: Context => Fu[ApiResult]) = Open { ctx =>
|
||||
js(ctx) map toHttp
|
||||
}
|
||||
def ApiRequest(js: RequestHeader => Fu[ApiResult]) = Action.async { req =>
|
||||
js(req) map toHttp
|
||||
}
|
||||
def MobileApiRequest(js: RequestHeader => Fu[ApiResult]) = Action.async { req =>
|
||||
if (lila.api.Mobile.Api requested req) js(req) map toHttp
|
||||
else fuccess(NotFound)
|
||||
}
|
||||
|
||||
lazy val tooManyRequests = Results.TooManyRequests(jsonError("Error 429: Too many requests! Try again later."))
|
||||
def toApiResult(json: Option[JsValue]): ApiResult = json.fold[ApiResult](NoData)(Data.apply)
|
||||
def toApiResult(json: Seq[JsValue]): ApiResult = Data(JsArray(json))
|
||||
|
||||
def toHttp(result: ApiResult): Result = result match {
|
||||
case Limited => tooManyRequests
|
||||
case NoData => NotFound
|
||||
case Custom(result) => result
|
||||
case Data(json) => Ok(json) as JSON
|
||||
}
|
||||
|
||||
def jsonStream(stream: Source[JsObject, _]): Result = jsonStringStream {
|
||||
stream.map { o => Json.stringify(o) + "\n" }
|
||||
}
|
||||
|
||||
def jsonOptionStream(stream: Source[Option[JsObject], _]): Result = jsonStringStream {
|
||||
stream.map { _ ?? Json.stringify + "\n" }
|
||||
}
|
||||
|
||||
def jsonStringStream(stream: Source[String, _]): Result =
|
||||
Ok.chunked(stream).withHeaders(CONTENT_TYPE -> ndJsonContentType) |> noProxyBuffer
|
||||
|
||||
private[controllers] val GlobalLinearLimitPerIP = new lila.memo.LinearLimit[IpAddress](
|
||||
name = "linear API per IP",
|
||||
key = "api.ip",
|
||||
|
@ -333,43 +368,13 @@ final class Api(
|
|||
user.fold(f) { u =>
|
||||
GlobalLinearLimitPerUser(u.id)(f)
|
||||
}
|
||||
}
|
||||
|
||||
private[controllers] object Api {
|
||||
|
||||
sealed trait ApiResult
|
||||
case class Data(json: JsValue) extends ApiResult
|
||||
case object NoData extends ApiResult
|
||||
case object Limited extends ApiResult
|
||||
case class Custom(result: Result) extends ApiResult
|
||||
def toApiResult(json: Option[JsValue]): ApiResult = json.fold[ApiResult](NoData)(Data.apply)
|
||||
def toApiResult(json: Seq[JsValue]): ApiResult = Data(JsArray(json))
|
||||
|
||||
def CookieBasedApiRequest(js: Context => Fu[ApiResult]) = Open { ctx =>
|
||||
js(ctx) map toHttp
|
||||
}
|
||||
def ApiRequest(js: RequestHeader => Fu[ApiResult]) = Action.async { req =>
|
||||
js(req) map toHttp
|
||||
}
|
||||
def MobileApiRequest(js: RequestHeader => Fu[ApiResult]) = Action.async { req =>
|
||||
if (lila.api.Mobile.Api requested req) js(req) map toHttp
|
||||
else fuccess(NotFound)
|
||||
}
|
||||
|
||||
private[controllers] val tooManyRequests = TooManyRequests(jsonError("Error 429: Too many requests! Try again later."))
|
||||
|
||||
private[controllers] def toHttp(result: ApiResult): Result = result match {
|
||||
case Limited => tooManyRequests
|
||||
case NoData => NotFound
|
||||
case Custom(result) => result
|
||||
case Data(json) => Ok(json) as JSON
|
||||
}
|
||||
|
||||
private[controllers] def jsonStream(stream: Source[JsObject, _]): Result = jsonStringStream {
|
||||
stream.map { o => Json.stringify(o) + "\n" }
|
||||
}
|
||||
|
||||
private[controllers] def jsonOptionStream(stream: Source[Option[JsObject], _]): Result = jsonStringStream {
|
||||
stream.map { _ ?? Json.stringify + "\n" }
|
||||
}
|
||||
|
||||
private def jsonStringStream(stream: Source[String, _]): Result =
|
||||
Ok.chunked(stream).withHeaders(CONTENT_TYPE -> ndJsonContentType) |> noProxyBuffer
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import views._
|
|||
|
||||
final class Auth(
|
||||
env: Env,
|
||||
accountC: Account
|
||||
accountC: => Account
|
||||
) extends LilaController(env) {
|
||||
|
||||
private def api = env.security.api
|
||||
|
|
|
@ -6,7 +6,7 @@ import lila.app._
|
|||
|
||||
final class Bot(
|
||||
env: Env,
|
||||
apiC: Api
|
||||
apiC: => Api
|
||||
) extends LilaController(env) {
|
||||
|
||||
def gameStream(id: String) = Scoped(_.Bot.Play) { req => me =>
|
||||
|
|
|
@ -12,7 +12,7 @@ import views.html
|
|||
|
||||
final class Challenge(
|
||||
env: Env,
|
||||
setupC: Setup
|
||||
setupC: => Setup
|
||||
) extends LilaController(env) {
|
||||
|
||||
def api = env.challenge.api
|
||||
|
|
|
@ -12,7 +12,7 @@ import lila.game.{ Game => GameModel }
|
|||
|
||||
final class Game(
|
||||
env: Env,
|
||||
apiC: Api
|
||||
apiC: => Api
|
||||
) extends LilaController(env) {
|
||||
|
||||
def delete(gameId: String) = Auth { implicit ctx => me =>
|
||||
|
|
|
@ -17,8 +17,8 @@ import play.api.mvc._
|
|||
|
||||
final class Mod(
|
||||
env: Env,
|
||||
reportC: Report,
|
||||
userC: User
|
||||
reportC: => Report,
|
||||
userC: => User
|
||||
) extends LilaController(env) {
|
||||
|
||||
private def modApi = env.mod.api
|
||||
|
|
|
@ -13,7 +13,7 @@ import views._
|
|||
|
||||
final class Practice(
|
||||
env: Env,
|
||||
userAnalysisC: UserAnalysis
|
||||
userAnalysisC: => UserAnalysis
|
||||
) extends LilaController(env) {
|
||||
|
||||
val api = env.practice.api
|
||||
|
|
|
@ -13,7 +13,7 @@ import views._
|
|||
|
||||
final class Puzzle(
|
||||
env: Env,
|
||||
apiC: Api
|
||||
apiC: => Api
|
||||
) extends LilaController(env) {
|
||||
|
||||
private def renderJson(
|
||||
|
|
|
@ -15,7 +15,7 @@ import views._
|
|||
|
||||
final class Relation(
|
||||
env: Env,
|
||||
apiC: Api
|
||||
apiC: => Api
|
||||
) extends LilaController(env) {
|
||||
|
||||
val api = env.relation.api
|
||||
|
@ -96,7 +96,6 @@ final class Relation(
|
|||
private def apiRelation(name: String, direction: Direction) = Action.async { req =>
|
||||
env.user.repo.named(name) flatMap {
|
||||
_ ?? { user =>
|
||||
import apiC.limitedDefault
|
||||
apiC.GlobalLinearLimitPerIP(HTTPRequest lastRemoteAddress req) {
|
||||
apiC.jsonStream {
|
||||
env.relation.stream
|
||||
|
|
|
@ -10,7 +10,7 @@ import views._
|
|||
|
||||
final class Relay(
|
||||
env: Env,
|
||||
studyC: Study
|
||||
studyC: => Study
|
||||
) extends LilaController(env) {
|
||||
|
||||
def index(page: Int) = Open { implicit ctx =>
|
||||
|
|
|
@ -12,8 +12,8 @@ import lila.user.{ User => UserModel }
|
|||
|
||||
final class Report(
|
||||
env: Env,
|
||||
userC: User,
|
||||
modC: Mod
|
||||
userC: => User,
|
||||
modC: => Mod
|
||||
) extends LilaController(env) {
|
||||
|
||||
private def api = env.report.api
|
||||
|
|
|
@ -14,10 +14,10 @@ import views._
|
|||
|
||||
final class Round(
|
||||
env: Env,
|
||||
gameC: Game,
|
||||
challengeC: Challenge,
|
||||
analyseC: Analyse,
|
||||
tournamentC: Tournament
|
||||
gameC: => Game,
|
||||
challengeC: => Challenge,
|
||||
analyseC: => Analyse,
|
||||
tournamentC: => Tournament
|
||||
) extends LilaController(env) with TheftPrevention {
|
||||
|
||||
private def analyser = env.analyse.analyser
|
||||
|
|
|
@ -8,7 +8,7 @@ import views._
|
|||
|
||||
final class Search(
|
||||
env: Env,
|
||||
apiC: Api
|
||||
apiC: => Api
|
||||
) extends LilaController(env) {
|
||||
|
||||
def searchForm = env.gameSearch.forms.search
|
||||
|
|
|
@ -17,7 +17,7 @@ import views._
|
|||
|
||||
final class Setup(
|
||||
env: Env,
|
||||
challengeC: Challenge
|
||||
challengeC: => Challenge
|
||||
) extends LilaController(env) with TheftPrevention {
|
||||
|
||||
private def forms = env.setup.forms
|
||||
|
|
|
@ -13,13 +13,12 @@ import views._
|
|||
|
||||
final class Simul(
|
||||
env: Env,
|
||||
teamC: Team
|
||||
teamC: => Team,
|
||||
apiC: => Team
|
||||
) extends LilaController(env) {
|
||||
|
||||
private def forms = lila.simul.SimulForm
|
||||
|
||||
import teamC.teamsIBelongTo
|
||||
|
||||
private def simulNotFound(implicit ctx: Context) = NotFound(html.simul.bits.notFound())
|
||||
|
||||
val home = Open { implicit ctx =>
|
||||
|
@ -124,7 +123,7 @@ final class Simul(
|
|||
|
||||
def form = Auth { implicit ctx => me =>
|
||||
NoLameOrBot {
|
||||
teamsIBelongTo(me) map { teams =>
|
||||
apiC.teamsIBelongTo(me) map { teams =>
|
||||
Ok(html.simul.form(forms.create, teams))
|
||||
}
|
||||
}
|
||||
|
@ -134,7 +133,7 @@ final class Simul(
|
|||
NoLameOrBot {
|
||||
implicit val req = ctx.body
|
||||
forms.create.bindFromRequest.fold(
|
||||
err => teamsIBelongTo(me) map { teams =>
|
||||
err => apiC.teamsIBelongTo(me) map { teams =>
|
||||
BadRequest(html.simul.form(err, teams))
|
||||
},
|
||||
setup => env.simul.api.create(setup, me) map { simul =>
|
||||
|
|
|
@ -9,7 +9,7 @@ import views._
|
|||
|
||||
final class Streamer(
|
||||
env: Env,
|
||||
apiC: Api
|
||||
apiC: => Api
|
||||
) extends LilaController(env) {
|
||||
|
||||
private def api = env.streamer.api
|
||||
|
|
|
@ -18,7 +18,7 @@ import views._
|
|||
|
||||
final class Study(
|
||||
env: Env,
|
||||
userAnalysisC: UserAnalysis
|
||||
userAnalysisC: => UserAnalysis
|
||||
) extends LilaController(env) {
|
||||
|
||||
def search(text: String, page: Int) = OpenBody { implicit ctx =>
|
||||
|
|
|
@ -15,7 +15,7 @@ import views._
|
|||
|
||||
final class Team(
|
||||
env: Env,
|
||||
apiC: Api
|
||||
apiC: => Api
|
||||
) extends LilaController(env) {
|
||||
|
||||
private def forms = env.team.forms
|
||||
|
@ -49,7 +49,6 @@ final class Team(
|
|||
} yield html.team.show(team, members, info)
|
||||
|
||||
def users(teamId: String) = Action.async { req =>
|
||||
import apiC.limitedDefault
|
||||
api.team(teamId) flatMap {
|
||||
_ ?? { team =>
|
||||
apiC.GlobalLinearLimitPerIP(HTTPRequest lastRemoteAddress req) {
|
||||
|
|
|
@ -16,7 +16,7 @@ import views._
|
|||
|
||||
final class Tournament(
|
||||
env: Env,
|
||||
teamC: Team
|
||||
teamC: => Team
|
||||
) extends LilaController(env) {
|
||||
|
||||
private def repo = env.tournament.tournamentRepo
|
||||
|
@ -24,8 +24,6 @@ final class Tournament(
|
|||
private def jsonView = env.tournament.jsonView
|
||||
private def forms = env.tournament.forms
|
||||
|
||||
import teamC.teamsIBelongTo
|
||||
|
||||
private def tournamentNotFound(implicit ctx: Context) = NotFound(html.tournament.bits.notFound())
|
||||
|
||||
private[controllers] val upcomingCache = env.memo.asyncCache.single[(VisibleTournaments, List[Tour])](
|
||||
|
@ -214,7 +212,7 @@ final class Tournament(
|
|||
|
||||
def form = Auth { implicit ctx => me =>
|
||||
NoLameOrBot {
|
||||
teamsIBelongTo(me) map { teams =>
|
||||
teamC.teamsIBelongTo(me) map { teams =>
|
||||
Ok(html.tournament.form(forms(me), forms, me, teams))
|
||||
}
|
||||
}
|
||||
|
@ -250,7 +248,7 @@ final class Tournament(
|
|||
|
||||
def create = AuthBody { implicit ctx => me =>
|
||||
NoLameOrBot {
|
||||
teamsIBelongTo(me) flatMap { teams =>
|
||||
teamC.teamsIBelongTo(me) flatMap { teams =>
|
||||
implicit val req = ctx.body
|
||||
negotiate(
|
||||
html = forms(me).bindFromRequest.fold(
|
||||
|
@ -284,7 +282,7 @@ final class Tournament(
|
|||
private def doApiCreate(me: lila.user.User)(implicit req: Request[_]): Fu[Result] =
|
||||
forms(me).bindFromRequest.fold(
|
||||
jsonFormErrorDefaultLang,
|
||||
setup => teamsIBelongTo(me) flatMap { teams =>
|
||||
setup => teamC.teamsIBelongTo(me) flatMap { teams =>
|
||||
api.createTournament(setup, me, teams, getUserTeamIds) flatMap { tour =>
|
||||
jsonView(tour, none, none, getUserTeamIds, env.team.getTeamName, none, none, partial = false, lila.i18n.defaultLang)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import views._
|
|||
|
||||
final class Tv(
|
||||
env: Env,
|
||||
apiC: Api
|
||||
apiC: => Api
|
||||
) extends LilaController(env) {
|
||||
|
||||
def index = onChannel(lila.tv.Tv.Channel.Best.key)
|
||||
|
@ -32,7 +32,7 @@ final class Tv(
|
|||
implicit val championWrites = Json.writes[lila.tv.Tv.Champion]
|
||||
env.tv.tv.getChampions map {
|
||||
_.channels map { case (chan, champ) => chan.name -> champ }
|
||||
} map { Json.toJson(_) } map apiC.Data.apply
|
||||
} map { Json.toJson(_) } map Api.Data.apply
|
||||
}
|
||||
|
||||
private def lichessTv(channel: lila.tv.Tv.Channel)(implicit ctx: Context) =
|
||||
|
|
|
@ -22,8 +22,8 @@ import views._
|
|||
|
||||
final class User(
|
||||
env: Env,
|
||||
roundC: Round,
|
||||
modC: Mod
|
||||
roundC: => Round,
|
||||
modC: => Mod
|
||||
) extends LilaController(env) {
|
||||
|
||||
private def relationApi = env.relation.api
|
||||
|
|
|
@ -18,7 +18,7 @@ import views._
|
|||
|
||||
final class UserAnalysis(
|
||||
env: Env,
|
||||
gameC: Game
|
||||
gameC: => Game
|
||||
) extends LilaController(env) with TheftPrevention {
|
||||
|
||||
def index = load("", Standard)
|
||||
|
|
|
@ -169,6 +169,7 @@ relation {
|
|||
}
|
||||
bookmark {
|
||||
collection.bookmark = bookmark
|
||||
paginator.max_per_page = ${game.paginator.max_per_page}
|
||||
actor.name = bookmark
|
||||
}
|
||||
geoip {
|
||||
|
|
|
@ -1,37 +1,49 @@
|
|||
package lila.api
|
||||
|
||||
import com.softwaremill.macwire._
|
||||
import io.methvin.play.autoconfig._
|
||||
import lila.common.config._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.common.config._
|
||||
|
||||
@Module
|
||||
final class ApiConfig(
|
||||
@ConfigName("api.token") val apiToken: Secret,
|
||||
@ConfigName("api.influx_event.endpoint") val influxEventEndpoint: String,
|
||||
@ConfigName("api.influx_event.env") val influxEventEnv: String,
|
||||
@ConfigName("app.stage") val isStage: Boolean,
|
||||
@ConfigName("prismic.api_url") val prismicApiUrl: String,
|
||||
@ConfigName("editor.animation.duration") val editorAnimationDuration: FiniteDuration,
|
||||
@ConfigName("explorer.endpoint") val explorerEndpoint: String,
|
||||
@ConfigName("explorer.tablebase.endpoint") val tablebaseEndpoint: String,
|
||||
val apiToken: Secret,
|
||||
val influxEventEndpoint: String,
|
||||
val influxEventEnv: String,
|
||||
val isStage: Boolean,
|
||||
val prismicApiUrl: String,
|
||||
val editorAnimationDuration: FiniteDuration,
|
||||
val explorerEndpoint: String,
|
||||
val tablebaseEndpoint: String,
|
||||
val accessibility: ApiConfig.Accessibility
|
||||
)
|
||||
|
||||
object ApiConfig {
|
||||
|
||||
final class Accessibility(
|
||||
@ConfigName("blind.cookie.name") val blindCookieName: String,
|
||||
@ConfigName("blind.cookie.max_age") val blindCookieMaxAge: FiniteDuration,
|
||||
@ConfigName("blind.cookie.salt") blindCookieSalt: Secret
|
||||
val blindCookieName: String,
|
||||
val blindCookieMaxAge: FiniteDuration,
|
||||
blindCookieSalt: Secret
|
||||
) {
|
||||
def hash(implicit ctx: lila.user.UserContext) = {
|
||||
import com.roundeights.hasher.Implicits._
|
||||
(ctx.userId | "anon").salt(blindCookieSalt.value).md5.hex
|
||||
}
|
||||
}
|
||||
private implicit val accessibilityLoader = AutoConfig.loader[Accessibility]
|
||||
implicit val loader = AutoConfig.loader[ApiConfig]
|
||||
|
||||
def loadFrom(c: play.api.Configuration) = new ApiConfig(
|
||||
c.get[Secret]("api.token"),
|
||||
c.get[String]("api.influx_event.endpoint"),
|
||||
c.get[String]("api.influx_event.env"),
|
||||
c.get[Boolean]("app.stage"),
|
||||
c.get[String]("prismic.api_url"),
|
||||
c.get[FiniteDuration]("editor.animation.duration"),
|
||||
c.get[String]("explorer.endpoint"),
|
||||
c.get[String]("explorer.tablebase.endpoint"),
|
||||
new Accessibility(
|
||||
c.get[String]("accessibility.blind.cookie.name"),
|
||||
c.get[FiniteDuration]("accessibility.blind.cookie.max_age"),
|
||||
c.get[Secret]("accessibility.blind.cookie.salt")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -46,7 +46,8 @@ final class Env(
|
|||
val mode: Mode
|
||||
)(implicit system: ActorSystem) {
|
||||
|
||||
val config = appConfig.get[ApiConfig]("")(ApiConfig.loader)
|
||||
val config = ApiConfig loadFrom appConfig
|
||||
import config.apiToken
|
||||
|
||||
lazy val pgnDump: PgnDump = wire[PgnDump]
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import play.api.Configuration
|
|||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
@Module
|
||||
private class PlanConfig(
|
||||
private class BlogConfig(
|
||||
@ConfigName("prismic.api_url") val apiUrl: String,
|
||||
@ConfigName("last_post_cache.ttl") val lastPostTtl: FiniteDuration
|
||||
)
|
||||
|
@ -18,7 +18,7 @@ final class Env(
|
|||
timelineApi: lila.timeline.EntryApi
|
||||
)(implicit system: akka.actor.ActorSystem, ws: play.api.libs.ws.WSClient) {
|
||||
|
||||
private val config = appConfig.get[PlanConfig]("plan")(AutoConfig.loader)
|
||||
private val config = appConfig.get[BlogConfig]("blog")(AutoConfig.loader)
|
||||
|
||||
lazy val api = new BlogApi(
|
||||
prismicUrl = config.apiUrl,
|
||||
|
|
|
@ -22,7 +22,7 @@ final class Env(
|
|||
gameRepo: lila.game.GameRepo
|
||||
)(implicit system: ActorSystem) {
|
||||
|
||||
private val config = appConfig.get[BookmarkConfig]("game")(AutoConfig.loader)
|
||||
private val config = appConfig.get[BookmarkConfig]("bookmark")(AutoConfig.loader)
|
||||
|
||||
private lazy val bookmarkColl = db(config.bookmarkCollName)
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ final class Env(
|
|||
def netDomain = netConfig.domain
|
||||
|
||||
private lazy val detectLanguageConfig =
|
||||
appConfig.get[DetectLanguage.Config]("detect_language.api")
|
||||
appConfig.get[DetectLanguage.Config]("detectlanguage.api")
|
||||
|
||||
lazy val detectLanguage = wire[DetectLanguage]
|
||||
}
|
||||
|
|
|
@ -30,5 +30,6 @@ object WMMatching {
|
|||
}
|
||||
}
|
||||
|
||||
private def lowLevel(nvertex: Int, pairScore: (Int, Int) => Option[Int]): List[(Int, Int)] = ???
|
||||
// #TODO implement
|
||||
private def lowLevel(nvertex: Int, pairScore: (Int, Int) => Option[Int]): List[(Int, Int)] = Nil
|
||||
}
|
||||
|
|
|
@ -2,27 +2,33 @@ package lila.db
|
|||
|
||||
import io.methvin.play.autoconfig._
|
||||
import play.api.Configuration
|
||||
import play.api.inject.ApplicationLifecycle
|
||||
import reactivemongo.api._
|
||||
import reactivemongo.api.commands.Command
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
import dsl.Coll
|
||||
import lila.common.Chronometer
|
||||
import lila.common.config._
|
||||
|
||||
class DbConfig(
|
||||
final class DbConfig(
|
||||
val uri: String
|
||||
)
|
||||
|
||||
final class Env(name: String, config: DbConfig) {
|
||||
final class Env(
|
||||
name: String,
|
||||
config: DbConfig,
|
||||
lifecycle: ApplicationLifecycle
|
||||
) {
|
||||
|
||||
private val driver = MongoDriver()
|
||||
private val parsedUri = MongoConnection.parseURI(config.uri).get
|
||||
// private val connection = Future.fromTry(parsedUri.flatMap(driver.connection(_, true)))
|
||||
private val dbName = parsedUri.db | "lichess"
|
||||
val conn = driver.connection(parsedUri, name.some, true).get
|
||||
registerDriverShutdownHook(driver, conn)
|
||||
private val db = Chronometer.syncEffect(
|
||||
Await.result(conn database dbName, 3.seconds)
|
||||
) { lap =>
|
||||
|
@ -44,14 +50,32 @@ final class Env(name: String, config: DbConfig) {
|
|||
import DbImage.DbImageBSONHandler
|
||||
def fetch(id: String): Fu[Option[DbImage]] = imageColl.byId[DbImage](id)
|
||||
}
|
||||
|
||||
private def registerDriverShutdownHook(
|
||||
mongoDriver: MongoDriver,
|
||||
connection: MongoConnection
|
||||
): Unit = lifecycle.addStopHook { () =>
|
||||
logger.info("ReactiveMongoApi stopping...")
|
||||
|
||||
Await.ready(connection.askClose()(10.seconds).map { _ =>
|
||||
logger.info("ReactiveMongoApi connections are stopped")
|
||||
}.andThen {
|
||||
case Failure(reason) =>
|
||||
reason.printStackTrace()
|
||||
mongoDriver.close() // Close anyway
|
||||
|
||||
case _ => mongoDriver.close()
|
||||
}, 12.seconds)
|
||||
}
|
||||
}
|
||||
|
||||
object Env {
|
||||
|
||||
implicit val configLoader = AutoConfig.loader[DbConfig]
|
||||
|
||||
def main(appConfig: Configuration) = new Env(
|
||||
def main(appConfig: Configuration, lifecycle: ApplicationLifecycle) = new Env(
|
||||
name = "main",
|
||||
config = appConfig.get[DbConfig]("mongodb")
|
||||
config = appConfig.get[DbConfig]("mongodb"),
|
||||
lifecycle = lifecycle
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,33 +3,27 @@ package lila.forum
|
|||
import lila.common.paginator._
|
||||
import lila.db.dsl._
|
||||
|
||||
final class CategApi(
|
||||
postApi: => PostApi,
|
||||
topicApi: => TopicApi,
|
||||
categRepo: CategRepo,
|
||||
topicRepo: TopicRepo,
|
||||
postRepo: PostRepo
|
||||
) {
|
||||
final class CategApi(env: Env) {
|
||||
|
||||
import BSONHandlers._
|
||||
|
||||
def list(teams: Iterable[String], troll: Boolean): Fu[List[CategView]] = for {
|
||||
categs <- categRepo withTeams teams
|
||||
categs <- env.categRepo withTeams teams
|
||||
views <- (categs map { categ =>
|
||||
postApi get (categ lastPostId troll) map { topicPost =>
|
||||
env.postApi get (categ lastPostId troll) map { topicPost =>
|
||||
CategView(categ, topicPost map {
|
||||
_ match {
|
||||
case (topic, post) => (topic, post, postApi lastPageOf topic)
|
||||
case (topic, post) => (topic, post, env.postApi lastPageOf topic)
|
||||
}
|
||||
}, troll)
|
||||
}
|
||||
}).sequenceFu
|
||||
} yield views
|
||||
|
||||
def teamNbPosts(slug: String): Fu[Int] = categRepo nbPosts teamSlug(slug)
|
||||
def teamNbPosts(slug: String): Fu[Int] = env.categRepo nbPosts teamSlug(slug)
|
||||
|
||||
def makeTeam(slug: String, name: String): Funit =
|
||||
categRepo.nextPosition flatMap { position =>
|
||||
env.categRepo.nextPosition flatMap { position =>
|
||||
val categ = Categ(
|
||||
_id = teamSlug(slug),
|
||||
name = name,
|
||||
|
@ -63,25 +57,25 @@ final class CategApi(
|
|||
categId = categ.id,
|
||||
modIcon = None
|
||||
)
|
||||
categRepo.coll.insert.one(categ).void >>
|
||||
postRepo.coll.insert.one(post).void >>
|
||||
topicRepo.coll.insert.one(topic withPost post).void >>
|
||||
categRepo.coll.update.one($id(categ.id), categ withTopic post).void
|
||||
env.categRepo.coll.insert.one(categ).void >>
|
||||
env.postRepo.coll.insert.one(post).void >>
|
||||
env.topicRepo.coll.insert.one(topic withPost post).void >>
|
||||
env.categRepo.coll.update.one($id(categ.id), categ withTopic post).void
|
||||
}
|
||||
|
||||
def show(slug: String, page: Int, troll: Boolean): Fu[Option[(Categ, Paginator[TopicView])]] =
|
||||
optionT(categRepo bySlug slug) flatMap { categ =>
|
||||
optionT(topicApi.paginator(categ, page, troll) map { (categ, _).some })
|
||||
optionT(env.categRepo bySlug slug) flatMap { categ =>
|
||||
optionT(env.topicApi.paginator(categ, page, troll) map { (categ, _).some })
|
||||
} run
|
||||
|
||||
def denormalize(categ: Categ): Funit = for {
|
||||
nbTopics <- topicRepo countByCateg categ
|
||||
nbPosts <- postRepo countByCateg categ
|
||||
lastPost <- postRepo lastByCateg categ
|
||||
nbTopicsTroll <- topicRepo withTroll true countByCateg categ
|
||||
nbPostsTroll <- postRepo withTroll true countByCateg categ
|
||||
lastPostTroll <- postRepo withTroll true lastByCateg categ
|
||||
_ <- categRepo.coll.update.one($id(categ.id), categ.copy(
|
||||
nbTopics <- env.topicRepo countByCateg categ
|
||||
nbPosts <- env.postRepo countByCateg categ
|
||||
lastPost <- env.postRepo lastByCateg categ
|
||||
nbTopicsTroll <- env.topicRepo withTroll true countByCateg categ
|
||||
nbPostsTroll <- env.postRepo withTroll true countByCateg categ
|
||||
lastPostTroll <- env.postRepo withTroll true lastByCateg categ
|
||||
_ <- env.categRepo.coll.update.one($id(categ.id), categ.copy(
|
||||
nbTopics = nbTopics,
|
||||
nbPosts = nbPosts,
|
||||
lastPostId = lastPost ?? (_.id),
|
||||
|
|
|
@ -42,20 +42,22 @@ final class Env(
|
|||
lazy val topicRepo = new TopicRepo(db(CollName("f_topic")))
|
||||
lazy val postRepo = new PostRepo(db(CollName("f_post")))
|
||||
|
||||
lazy val categApi: CategApi = wire[CategApi]
|
||||
|
||||
lazy val mentionNotifier: MentionNotifier = wire[MentionNotifier]
|
||||
lazy val categApi: CategApi = {
|
||||
val mk = (env: Env) => wire[CategApi]
|
||||
mk(this)
|
||||
}
|
||||
|
||||
lazy val topicApi: TopicApi = {
|
||||
val mk = (max: MaxPerPage) => wire[TopicApi]
|
||||
mk(config.topicMaxPerPage)
|
||||
val mk = (max: MaxPerPage, env: Env) => wire[TopicApi]
|
||||
mk(config.topicMaxPerPage, this)
|
||||
}
|
||||
|
||||
lazy val postApi: PostApi = {
|
||||
val mk = (max: MaxPerPage) => wire[PostApi]
|
||||
mk(config.postMaxPerPage)
|
||||
val mk = (max: MaxPerPage, env: Env) => wire[PostApi]
|
||||
mk(config.postMaxPerPage, this)
|
||||
}
|
||||
|
||||
lazy val mentionNotifier: MentionNotifier = wire[MentionNotifier]
|
||||
lazy val forms = wire[DataForm]
|
||||
lazy val recent = wire[Recent]
|
||||
|
||||
|
|
|
@ -12,20 +12,14 @@ import lila.user.{ User, UserContext }
|
|||
import org.joda.time.DateTime
|
||||
|
||||
final class PostApi(
|
||||
categApi: => CategApi,
|
||||
topicApi: => TopicApi,
|
||||
categRepo: CategRepo,
|
||||
topicRepo: TopicRepo,
|
||||
postRepo: PostRepo,
|
||||
recent: Recent,
|
||||
env: Env,
|
||||
indexer: lila.hub.actors.ForumSearch,
|
||||
maxPerPage: lila.common.config.MaxPerPage,
|
||||
modLog: ModlogApi,
|
||||
spam: lila.security.Spam,
|
||||
timeline: lila.hub.actors.Timeline,
|
||||
shutup: lila.hub.actors.Shutup,
|
||||
detectLanguage: lila.common.DetectLanguage,
|
||||
mentionNotifier: MentionNotifier
|
||||
detectLanguage: lila.common.DetectLanguage
|
||||
) {
|
||||
|
||||
import BSONHandlers._
|
||||
|
@ -50,16 +44,16 @@ final class PostApi(
|
|||
categId = categ.id,
|
||||
modIcon = (~data.modIcon && ~ctx.me.map(MasterGranter(_.PublicMod))).option(true)
|
||||
)
|
||||
postRepo findDuplicate post flatMap {
|
||||
env.postRepo findDuplicate post flatMap {
|
||||
case Some(dup) => fuccess(dup)
|
||||
case _ =>
|
||||
postRepo.coll.insert.one(post) >>
|
||||
topicRepo.coll.update.one($id(topic.id), topic withPost post) >> {
|
||||
shouldHideOnPost(topic) ?? topicRepo.hide(topic.id, true)
|
||||
env.postRepo.coll.insert.one(post) >>
|
||||
env.topicRepo.coll.update.one($id(topic.id), topic withPost post) >> {
|
||||
shouldHideOnPost(topic) ?? env.topicRepo.hide(topic.id, true)
|
||||
} >>
|
||||
categRepo.coll.update.one($id(categ.id), categ withTopic post) >>-
|
||||
env.categRepo.coll.update.one($id(categ.id), categ withTopic post) >>-
|
||||
(!categ.quiet ?? (indexer ! InsertPost(post))) >>-
|
||||
(!categ.quiet ?? recent.invalidate) >>-
|
||||
(!categ.quiet ?? env.recent.invalidate) >>-
|
||||
ctx.userId.?? { userId =>
|
||||
shutup ! {
|
||||
if (post.isTeam) lila.hub.actorApi.shutup.RecordTeamForumMessage(userId, post.text)
|
||||
|
@ -72,7 +66,7 @@ final class PostApi(
|
|||
}
|
||||
}
|
||||
lila.mon.forum.post.create()
|
||||
mentionNotifier.notifyMentionedUsers(post, topic)
|
||||
env.mentionNotifier.notifyMentionedUsers(post, topic)
|
||||
Bus.publish(actorApi.CreatePost(post, topic), "forumPost")
|
||||
} inject post
|
||||
}
|
||||
|
@ -87,7 +81,7 @@ final class PostApi(
|
|||
fufail("Post can no longer be edited")
|
||||
case (_, post) =>
|
||||
val newPost = post.editPost(DateTime.now, spam replace newText)
|
||||
postRepo.coll.update.one($id(post.id), newPost) inject newPost
|
||||
env.postRepo.coll.update.one($id(post.id), newPost) inject newPost
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +97,7 @@ final class PostApi(
|
|||
|
||||
def urlData(postId: String, troll: Boolean): Fu[Option[PostUrlData]] = get(postId) flatMap {
|
||||
case Some((topic, post)) if (!troll && post.troll) => fuccess(none[PostUrlData])
|
||||
case Some((topic, post)) => postRepo.withTroll(troll).countBeforeNumber(topic.id, post.number) map { nb =>
|
||||
case Some((topic, post)) => env.postRepo.withTroll(troll).countBeforeNumber(topic.id, post.number) map { nb =>
|
||||
val page = nb / maxPerPage.value + 1
|
||||
PostUrlData(topic.categId, topic.slug, page, post.number).some
|
||||
}
|
||||
|
@ -112,14 +106,14 @@ final class PostApi(
|
|||
|
||||
def get(postId: String): Fu[Option[(Topic, Post)]] = {
|
||||
for {
|
||||
post <- optionT(postRepo.coll.byId[Post](postId))
|
||||
topic <- optionT(topicRepo.coll.byId[Topic](post.topicId))
|
||||
post <- optionT(env.postRepo.coll.byId[Post](postId))
|
||||
topic <- optionT(env.topicRepo.coll.byId[Topic](post.topicId))
|
||||
} yield topic -> post
|
||||
} run
|
||||
|
||||
def views(posts: List[Post]): Fu[List[PostView]] = for {
|
||||
topics <- topicRepo.coll.byIds[Topic](posts.map(_.topicId).distinct)
|
||||
categs <- categRepo.coll.byIds[Categ](topics.map(_.categId).distinct)
|
||||
topics <- env.topicRepo.coll.byIds[Topic](posts.map(_.topicId).distinct)
|
||||
categs <- env.categRepo.coll.byIds[Categ](topics.map(_.categId).distinct)
|
||||
} yield posts map { post =>
|
||||
for {
|
||||
topic <- topics find (_.id == post.topicId)
|
||||
|
@ -128,27 +122,27 @@ final class PostApi(
|
|||
} flatten
|
||||
|
||||
def viewsFromIds(postIds: Seq[Post.ID]): Fu[List[PostView]] =
|
||||
postRepo.coll.byOrderedIds[Post, Post.ID](postIds)(_.id) flatMap views
|
||||
env.postRepo.coll.byOrderedIds[Post, Post.ID](postIds)(_.id) flatMap views
|
||||
|
||||
def view(post: Post): Fu[Option[PostView]] =
|
||||
views(List(post)) map (_.headOption)
|
||||
|
||||
def liteViews(posts: Seq[Post]): Fu[Seq[PostLiteView]] =
|
||||
for {
|
||||
topics <- topicRepo.coll.byIds[Topic](posts.map(_.topicId).distinct)
|
||||
topics <- env.topicRepo.coll.byIds[Topic](posts.map(_.topicId).distinct)
|
||||
} yield posts flatMap { post =>
|
||||
topics.find(_.id == post.topicId) map { topic =>
|
||||
PostLiteView(post, topic)
|
||||
}
|
||||
}
|
||||
def liteViewsByIds(postIds: Seq[Post.ID]): Fu[Seq[PostLiteView]] =
|
||||
postRepo.byIds(postIds) flatMap liteViews
|
||||
env.postRepo.byIds(postIds) flatMap liteViews
|
||||
|
||||
def liteView(post: Post): Fu[Option[PostLiteView]] =
|
||||
liteViews(List(post)) map (_.headOption)
|
||||
|
||||
def miniPosts(posts: List[Post]): Fu[List[MiniForumPost]] = for {
|
||||
topics <- topicRepo.coll.byIds[Topic](posts.map(_.topicId).distinct)
|
||||
topics <- env.topicRepo.coll.byIds[Topic](posts.map(_.topicId).distinct)
|
||||
} yield posts flatMap { post =>
|
||||
topics find (_.id == post.topicId) map { topic =>
|
||||
MiniForumPost(
|
||||
|
@ -163,45 +157,45 @@ final class PostApi(
|
|||
}
|
||||
|
||||
def lastNumberOf(topic: Topic): Fu[Int] =
|
||||
postRepo lastByTopic topic map { _ ?? (_.number) }
|
||||
env.postRepo lastByTopic topic map { _ ?? (_.number) }
|
||||
|
||||
def lastPageOf(topic: Topic) =
|
||||
math.ceil(topic.nbPosts / maxPerPage.value.toFloat).toInt
|
||||
|
||||
def paginator(topic: Topic, page: Int, troll: Boolean): Fu[Paginator[Post]] = Paginator(
|
||||
new Adapter(
|
||||
collection = postRepo.coll,
|
||||
selector = postRepo.withTroll(troll) selectTopic topic.id,
|
||||
collection = env.postRepo.coll,
|
||||
selector = env.postRepo.withTroll(troll) selectTopic topic.id,
|
||||
projection = none,
|
||||
sort = postRepo.sortQuery
|
||||
sort = env.postRepo.sortQuery
|
||||
),
|
||||
currentPage = page,
|
||||
maxPerPage = maxPerPage
|
||||
)
|
||||
|
||||
def delete(categSlug: String, postId: String, mod: User): Funit = (for {
|
||||
post <- optionT(postRepo.withTroll(true).byCategAndId(categSlug, postId))
|
||||
post <- optionT(env.postRepo.withTroll(true).byCategAndId(categSlug, postId))
|
||||
view <- optionT(view(post))
|
||||
_ <- optionT(for {
|
||||
first <- postRepo.isFirstPost(view.topic.id, view.post.id)
|
||||
_ <- if (first) topicApi.delete(view.categ, view.topic)
|
||||
else postRepo.coll.delete.one(view.post) >>
|
||||
(topicApi denormalize view.topic) >>
|
||||
(categApi denormalize view.categ) >>-
|
||||
recent.invalidate >>-
|
||||
first <- env.postRepo.isFirstPost(view.topic.id, view.post.id)
|
||||
_ <- if (first) env.topicApi.delete(view.categ, view.topic)
|
||||
else env.postRepo.coll.delete.one(view.post) >>
|
||||
(env.topicApi denormalize view.topic) >>
|
||||
(env.categApi denormalize view.categ) >>-
|
||||
env.recent.invalidate >>-
|
||||
(indexer ! RemovePost(post.id))
|
||||
_ <- MasterGranter(_.ModerateForum)(mod) ?? modLog.deletePost(mod.id, post.userId, post.author, post.ip,
|
||||
text = "%s / %s / %s".format(view.categ.name, view.topic.name, post.text))
|
||||
} yield true.some)
|
||||
} yield ()).run.void
|
||||
|
||||
def nbByUser(userId: String) = postRepo.coll.countSel($doc("userId" -> userId))
|
||||
def nbByUser(userId: String) = env.postRepo.coll.countSel($doc("userId" -> userId))
|
||||
|
||||
def userIds(topic: Topic) = postRepo userIdsByTopicId topic.id
|
||||
def userIds(topic: Topic) = env.postRepo userIdsByTopicId topic.id
|
||||
|
||||
def userIds(topicId: String) = postRepo userIdsByTopicId topicId
|
||||
def userIds(topicId: String) = env.postRepo userIdsByTopicId topicId
|
||||
|
||||
def erase(user: User) = postRepo.coll.update.one(
|
||||
def erase(user: User) = env.postRepo.coll.update.one(
|
||||
$doc("userId" -> user.id),
|
||||
$unset("userId", "editHistory", "lang", "ip") ++
|
||||
$set("text" -> "", "erasedAt" -> DateTime.now),
|
||||
|
|
|
@ -11,20 +11,14 @@ import lila.security.{ Granter => MasterGranter }
|
|||
import lila.user.{ User, UserContext }
|
||||
|
||||
private[forum] final class TopicApi(
|
||||
postApi: => PostApi,
|
||||
categApi: => CategApi,
|
||||
categRepo: CategRepo,
|
||||
topicRepo: TopicRepo,
|
||||
postRepo: PostRepo,
|
||||
recent: Recent,
|
||||
env: Env,
|
||||
indexer: lila.hub.actors.ForumSearch,
|
||||
maxPerPage: lila.common.config.MaxPerPage,
|
||||
modLog: lila.mod.ModlogApi,
|
||||
spam: lila.security.Spam,
|
||||
timeline: lila.hub.actors.Timeline,
|
||||
shutup: lila.hub.actors.Shutup,
|
||||
detectLanguage: lila.common.DetectLanguage,
|
||||
mentionNotifier: MentionNotifier
|
||||
detectLanguage: lila.common.DetectLanguage
|
||||
) {
|
||||
|
||||
import BSONHandlers._
|
||||
|
@ -32,14 +26,14 @@ private[forum] final class TopicApi(
|
|||
def show(categSlug: String, slug: String, page: Int, troll: Boolean): Fu[Option[(Categ, Topic, Paginator[Post])]] =
|
||||
for {
|
||||
data <- (for {
|
||||
categ <- optionT(categRepo bySlug categSlug)
|
||||
topic <- optionT(topicRepo.withTroll(troll).byTree(categSlug, slug))
|
||||
categ <- optionT(env.categRepo bySlug categSlug)
|
||||
topic <- optionT(env.topicRepo.withTroll(troll).byTree(categSlug, slug))
|
||||
} yield categ -> topic).run
|
||||
res <- data ?? {
|
||||
case (categ, topic) =>
|
||||
lila.mon.forum.topic.view()
|
||||
topicRepo incViews topic
|
||||
postApi.paginator(topic, page, troll) map { (categ, topic, _).some }
|
||||
env.topicRepo incViews topic
|
||||
env.postApi.paginator(topic, page, troll) map { (categ, topic, _).some }
|
||||
}
|
||||
} yield res
|
||||
|
||||
|
@ -47,7 +41,7 @@ private[forum] final class TopicApi(
|
|||
categ: Categ,
|
||||
data: DataForm.TopicData
|
||||
)(implicit ctx: UserContext): Fu[Topic] =
|
||||
topicRepo.nextSlug(categ, data.name) zip detectLanguage(data.post.text) flatMap {
|
||||
env.topicRepo.nextSlug(categ, data.name) zip detectLanguage(data.post.text) flatMap {
|
||||
case (slug, lang) =>
|
||||
val topic = Topic.make(
|
||||
categId = categ.slug,
|
||||
|
@ -69,11 +63,11 @@ private[forum] final class TopicApi(
|
|||
categId = categ.id,
|
||||
modIcon = (~data.post.modIcon && ~ctx.me.map(MasterGranter(_.PublicMod))).option(true)
|
||||
)
|
||||
postRepo.coll.insert.one(post) >>
|
||||
topicRepo.coll.insert.one(topic withPost post) >>
|
||||
categRepo.coll.update.one($id(categ.id), categ withTopic post) >>-
|
||||
env.postRepo.coll.insert.one(post) >>
|
||||
env.topicRepo.coll.insert.one(topic withPost post) >>
|
||||
env.categRepo.coll.update.one($id(categ.id), categ withTopic post) >>-
|
||||
(!categ.quiet ?? (indexer ! InsertPost(post))) >>-
|
||||
(!categ.quiet ?? recent.invalidate) >>-
|
||||
(!categ.quiet ?? env.recent.invalidate) >>-
|
||||
ctx.userId.?? { userId =>
|
||||
val text = s"${topic.name} ${post.text}"
|
||||
shutup ! {
|
||||
|
@ -86,7 +80,7 @@ private[forum] final class TopicApi(
|
|||
}
|
||||
lila.mon.forum.post.create()
|
||||
} >>- {
|
||||
mentionNotifier.notifyMentionedUsers(post, topic)
|
||||
env.mentionNotifier.notifyMentionedUsers(post, topic)
|
||||
Bus.publish(actorApi.CreatePost(post, topic), "forumPost")
|
||||
} inject topic
|
||||
}
|
||||
|
@ -112,24 +106,24 @@ private[forum] final class TopicApi(
|
|||
categId = categ.id,
|
||||
modIcon = true.some
|
||||
)
|
||||
postRepo.coll.insert.one(post) >>
|
||||
topicRepo.coll.insert.one(topic withPost post) >>
|
||||
categRepo.coll.update.one($id(categ.id), categ withTopic post) >>-
|
||||
env.postRepo.coll.insert.one(post) >>
|
||||
env.topicRepo.coll.insert.one(topic withPost post) >>
|
||||
env.categRepo.coll.update.one($id(categ.id), categ withTopic post) >>-
|
||||
(indexer ! InsertPost(post)) >>-
|
||||
recent.invalidate >>-
|
||||
env.recent.invalidate >>-
|
||||
Bus.publish(actorApi.CreatePost(post, topic), "forumPost") void
|
||||
}
|
||||
|
||||
def paginator(categ: Categ, page: Int, troll: Boolean): Fu[Paginator[TopicView]] = {
|
||||
val adapter = new Adapter[Topic](
|
||||
collection = topicRepo.coll,
|
||||
selector = topicRepo.withTroll(troll) byCategNotStickyQuery categ,
|
||||
collection = env.topicRepo.coll,
|
||||
selector = env.topicRepo.withTroll(troll) byCategNotStickyQuery categ,
|
||||
projection = none,
|
||||
sort = $sort.updatedDesc
|
||||
) mapFutureList { topics =>
|
||||
postRepo.coll.optionsByOrderedIds[Post, String](topics.map(_ lastPostId troll))(_.id) map { posts =>
|
||||
env.postRepo.coll.optionsByOrderedIds[Post, String](topics.map(_ lastPostId troll))(_.id) map { posts =>
|
||||
topics zip posts map {
|
||||
case topic ~ post => TopicView(categ, topic, post, postApi lastPageOf topic, troll)
|
||||
case topic ~ post => TopicView(categ, topic, post, env.postApi lastPageOf topic, troll)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -144,48 +138,48 @@ private[forum] final class TopicApi(
|
|||
}
|
||||
|
||||
def getSticky(categ: Categ, troll: Boolean): Fu[List[TopicView]] =
|
||||
topicRepo.stickyByCateg(categ) flatMap { topics =>
|
||||
env.topicRepo.stickyByCateg(categ) flatMap { topics =>
|
||||
topics.map { topic =>
|
||||
postRepo.coll.byId[Post](topic lastPostId troll) map { post =>
|
||||
TopicView(categ, topic, post, postApi lastPageOf topic, troll)
|
||||
env.postRepo.coll.byId[Post](topic lastPostId troll) map { post =>
|
||||
TopicView(categ, topic, post, env.postApi lastPageOf topic, troll)
|
||||
}
|
||||
}.sequenceFu
|
||||
}
|
||||
|
||||
def delete(categ: Categ, topic: Topic): Funit =
|
||||
postRepo.idsByTopicId(topic.id) flatMap { postIds =>
|
||||
(postRepo removeByTopic topic.id zip topicRepo.coll.delete.one($id(topic.id))) >>
|
||||
(categApi denormalize categ) >>-
|
||||
env.postRepo.idsByTopicId(topic.id) flatMap { postIds =>
|
||||
(env.postRepo removeByTopic topic.id zip env.topicRepo.coll.delete.one($id(topic.id))) >>
|
||||
(env.categApi denormalize categ) >>-
|
||||
(indexer ! RemovePosts(postIds)) >>-
|
||||
recent.invalidate
|
||||
env.recent.invalidate
|
||||
}
|
||||
|
||||
def toggleClose(categ: Categ, topic: Topic, mod: User): Funit =
|
||||
topicRepo.close(topic.id, topic.open) >> {
|
||||
env.topicRepo.close(topic.id, topic.open) >> {
|
||||
MasterGranter(_.ModerateForum)(mod) ??
|
||||
modLog.toggleCloseTopic(mod.id, categ.name, topic.name, topic.open)
|
||||
}
|
||||
|
||||
def toggleHide(categ: Categ, topic: Topic, mod: User): Funit =
|
||||
topicRepo.hide(topic.id, topic.visibleOnHome) >> {
|
||||
env.topicRepo.hide(topic.id, topic.visibleOnHome) >> {
|
||||
MasterGranter(_.ModerateForum)(mod) ?? {
|
||||
postRepo.hideByTopic(topic.id, topic.visibleOnHome) >>
|
||||
env.postRepo.hideByTopic(topic.id, topic.visibleOnHome) >>
|
||||
modLog.toggleHideTopic(mod.id, categ.name, topic.name, topic.visibleOnHome)
|
||||
} >>- recent.invalidate
|
||||
} >>- env.recent.invalidate
|
||||
}
|
||||
|
||||
def toggleSticky(categ: Categ, topic: Topic, mod: User): Funit =
|
||||
topicRepo.sticky(topic.id, !topic.isSticky) >> {
|
||||
env.topicRepo.sticky(topic.id, !topic.isSticky) >> {
|
||||
MasterGranter(_.ModerateForum)(mod) ??
|
||||
modLog.toggleStickyTopic(mod.id, categ.name, topic.name, topic.isSticky)
|
||||
}
|
||||
|
||||
def denormalize(topic: Topic): Funit = for {
|
||||
nbPosts <- postRepo countByTopic topic
|
||||
lastPost <- postRepo lastByTopic topic
|
||||
nbPostsTroll <- postRepo withTroll true countByTopic topic
|
||||
lastPostTroll <- postRepo withTroll true lastByTopic topic
|
||||
_ <- topicRepo.coll.update.one($id(topic.id), topic.copy(
|
||||
nbPosts <- env.postRepo countByTopic topic
|
||||
lastPost <- env.postRepo lastByTopic topic
|
||||
nbPostsTroll <- env.postRepo withTroll true countByTopic topic
|
||||
lastPostTroll <- env.postRepo withTroll true lastByTopic topic
|
||||
_ <- env.topicRepo.coll.update.one($id(topic.id), topic.copy(
|
||||
nbPosts = nbPosts,
|
||||
lastPostId = lastPost ?? (_.id),
|
||||
updatedAt = lastPost.fold(topic.updatedAt)(_.createdAt),
|
||||
|
|
|
@ -11,7 +11,7 @@ import Query.jsonWriter
|
|||
|
||||
@Module
|
||||
private class ForumSearchConfig(
|
||||
@ConfigName("index.name") val indexName: String,
|
||||
@ConfigName("index") val indexName: String,
|
||||
@ConfigName("paginator.max_per_page") val maxPerPage: MaxPerPage,
|
||||
@ConfigName("actor.name") val actorName: String
|
||||
)
|
||||
|
|
|
@ -10,8 +10,7 @@ import scala.concurrent.duration._
|
|||
|
||||
import lila.common.config._
|
||||
|
||||
@Module
|
||||
private class GameConfig(
|
||||
private final class GameConfig(
|
||||
@ConfigName("collection.game") val gameColl: CollName,
|
||||
@ConfigName("collection.crosstable") val crosstableColl: CollName,
|
||||
@ConfigName("collection.matchup") val matchupColl: CollName,
|
||||
|
@ -38,6 +37,7 @@ final class Env(
|
|||
)(implicit system: ActorSystem, scheduler: Scheduler) {
|
||||
|
||||
private val config = appConfig.get[GameConfig]("game")(AutoConfig.loader)
|
||||
import config.paginatorMaxPerPage
|
||||
|
||||
lazy val gameRepo = new GameRepo(db(config.gameColl))
|
||||
|
||||
|
|
|
@ -15,12 +15,14 @@ final class Env(
|
|||
gameRepo: lila.game.GameRepo,
|
||||
analysisRepo: lila.analyse.AnalysisRepo,
|
||||
prefApi: lila.pref.PrefApi,
|
||||
relationApi: lila.relation.RelationApi
|
||||
relationApi: lila.relation.RelationApi,
|
||||
lifecycle: play.api.inject.ApplicationLifecycle
|
||||
)(implicit system: akka.actor.ActorSystem) {
|
||||
|
||||
private lazy val db = new lila.db.Env(
|
||||
"insight",
|
||||
appConfig.get[DbConfig]("insight.mongodb")(AutoConfig.loader)
|
||||
appConfig.get[DbConfig]("insight.mongodb")(AutoConfig.loader),
|
||||
lifecycle
|
||||
)
|
||||
|
||||
lazy val share = wire[Share]
|
||||
|
|
|
@ -22,13 +22,12 @@ final class Env(
|
|||
appConfig: Configuration,
|
||||
asyncCache: lila.memo.AsyncCache.Builder,
|
||||
userRepo: lila.user.UserRepo,
|
||||
system: ActorSystem,
|
||||
lifecycle: play.api.inject.ApplicationLifecycle
|
||||
) {
|
||||
)(implicit system: ActorSystem) {
|
||||
|
||||
private val config = appConfig.get[OauthConfig]("oauth")(AutoConfig.loader)
|
||||
|
||||
private lazy val db = new lila.db.Env("oauth", config.mongodb)
|
||||
private lazy val db = new lila.db.Env("oauth", config.mongodb, lifecycle)
|
||||
private lazy val tokenColl = db(config.tokenColl)
|
||||
private lazy val appColl = db(config.appColl)
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ private object OneSignalPush {
|
|||
|
||||
final class Config(
|
||||
val url: String,
|
||||
val appId: String,
|
||||
@ConfigName("app_id") val appId: String,
|
||||
val key: lila.common.config.Secret
|
||||
)
|
||||
implicit val configLoader = AutoConfig.loader[Config]
|
||||
|
|
|
@ -49,7 +49,7 @@ private object WebPush {
|
|||
|
||||
final class Config(
|
||||
val url: String,
|
||||
val vapidPublicKey: String
|
||||
@ConfigName("vapid_public_key") val vapidPublicKey: String
|
||||
)
|
||||
implicit val configLoader = AutoConfig.loader[Config]
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import lila.common.config._
|
|||
import lila.db.Env.configLoader
|
||||
|
||||
@Module
|
||||
private class CoachConfig(
|
||||
private class PuzzleConfig(
|
||||
val mongodb: lila.db.DbConfig,
|
||||
@ConfigName("collection.puzzle") val puzzleColl: CollName,
|
||||
@ConfigName("collection.round") val roundColl: CollName,
|
||||
|
@ -28,12 +28,13 @@ final class Env(
|
|||
lightUserApi: lila.user.LightUserApi,
|
||||
asyncCache: lila.memo.AsyncCache.Builder,
|
||||
gameRepo: lila.game.GameRepo,
|
||||
userRepo: lila.user.UserRepo
|
||||
userRepo: lila.user.UserRepo,
|
||||
lifecycle: play.api.inject.ApplicationLifecycle
|
||||
)(implicit system: ActorSystem) {
|
||||
|
||||
private val config = appConfig.get[CoachConfig]("coach")(AutoConfig.loader)
|
||||
private val config = appConfig.get[PuzzleConfig]("puzzle")(AutoConfig.loader)
|
||||
|
||||
private lazy val db = new lila.db.Env("puzzle", config.mongodb)
|
||||
private lazy val db = new lila.db.Env("puzzle", config.mongodb, lifecycle)
|
||||
|
||||
private lazy val gameJson = wire[GameJson]
|
||||
|
||||
|
|
Loading…
Reference in New Issue