206 lines
6.3 KiB
Scala
206 lines
6.3 KiB
Scala
package controllers
|
|
|
|
import play.api.data.Form
|
|
import play.api.mvc._
|
|
import scala.annotation.nowarn
|
|
import scala.concurrent.duration._
|
|
import scala.util.chaining._
|
|
import views._
|
|
|
|
import lila.api.Context
|
|
import lila.app._
|
|
import lila.common.config.MaxPerSecond
|
|
import lila.common.{ HTTPRequest, IpAddress }
|
|
import lila.relay.{ RelayRound => RoundModel, RelayTour => TourModel }
|
|
import lila.user.{ User => UserModel }
|
|
|
|
final class RelayTour(env: Env, apiC: => Api) extends LilaController(env) {
|
|
|
|
def index(page: Int) =
|
|
Open { implicit ctx =>
|
|
Reasonable(page) {
|
|
for {
|
|
active <- (page == 1).??(env.relay.api.officialActive)
|
|
pager <- env.relay.pager.inactive(page)
|
|
} yield Ok(html.relay.tour.index(active, pager))
|
|
}
|
|
}
|
|
|
|
def form = Auth { implicit ctx => me =>
|
|
NoLameOrBot {
|
|
Ok(html.relay.tourForm.create(env.relay.tourForm.create)).fuccess
|
|
}
|
|
}
|
|
|
|
def create =
|
|
AuthOrScopedBody(_.Study.Write)(
|
|
auth = implicit ctx =>
|
|
me =>
|
|
NoLameOrBot {
|
|
env.relay.tourForm.create
|
|
.bindFromRequest()(ctx.body, formBinding)
|
|
.fold(
|
|
err => BadRequest(html.relay.tourForm.create(err)).fuccess,
|
|
setup =>
|
|
rateLimitCreation(me, ctx.req, Redirect(routes.RelayTour.index())) {
|
|
env.relay.api.tourCreate(setup, me) map { tour =>
|
|
Redirect(routes.RelayRound.form(tour.id.value)).flashSuccess
|
|
}
|
|
}
|
|
)
|
|
},
|
|
scoped = req =>
|
|
me =>
|
|
NoLameOrBot(me) {
|
|
env.relay.tourForm.create
|
|
.bindFromRequest()(req, formBinding)
|
|
.fold(
|
|
err => BadRequest(apiFormError(err)).fuccess,
|
|
setup =>
|
|
rateLimitCreation(me, req, rateLimited) {
|
|
JsonOk {
|
|
env.relay.api.tourCreate(setup, me) map { tour =>
|
|
env.relay.jsonView(tour.withRounds(Nil), withUrls = true)
|
|
}
|
|
}
|
|
}
|
|
)
|
|
}
|
|
)
|
|
|
|
def edit(id: String) = Auth { implicit ctx => me =>
|
|
WithTourCanUpdate(id) { tour =>
|
|
Ok(html.relay.tourForm.edit(tour, env.relay.tourForm.edit(tour))).fuccess
|
|
}
|
|
}
|
|
|
|
def update(id: String) =
|
|
AuthOrScopedBody(_.Study.Write)(
|
|
auth = implicit ctx =>
|
|
me =>
|
|
WithTourCanUpdate(id) { tour =>
|
|
env.relay.tourForm
|
|
.edit(tour)
|
|
.bindFromRequest()(ctx.body, formBinding)
|
|
.fold(
|
|
err => BadRequest(html.relay.tourForm.edit(tour, err)).fuccess,
|
|
setup =>
|
|
env.relay.api.tourUpdate(tour, setup, me) inject
|
|
Redirect(routes.RelayTour.redirectOrApiTour(tour.slug, tour.id.value))
|
|
)
|
|
},
|
|
scoped = implicit req =>
|
|
me =>
|
|
env.relay.api tourById TourModel.Id(id) flatMap {
|
|
_ ?? { tour =>
|
|
env.relay.api.canUpdate(me, tour) flatMap {
|
|
_ ?? env.relay.tourForm
|
|
.edit(tour)
|
|
.bindFromRequest()
|
|
.fold(
|
|
err => BadRequest(apiFormError(err)).fuccess,
|
|
setup => env.relay.api.tourUpdate(tour, setup, me) inject jsonOkResult
|
|
)
|
|
}
|
|
}
|
|
}
|
|
)
|
|
|
|
def redirectOrApiTour(@nowarn("msg=unused") slug: String, anyId: String) = Open { implicit ctx =>
|
|
env.relay.api byIdWithTour RoundModel.Id(anyId) flatMap {
|
|
case Some(rt) => Redirect(rt.path).fuccess // BC old broadcast URLs
|
|
case None =>
|
|
env.relay.api tourById TourModel.Id(anyId) flatMap {
|
|
_ ?? { tour =>
|
|
render.async {
|
|
case Accepts.Json() =>
|
|
JsonOk {
|
|
env.relay.api.withRounds(tour) map { trs =>
|
|
env.relay.jsonView(trs, withUrls = true)
|
|
}
|
|
}
|
|
case _ => redirectToTour(tour)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def pgn(id: String) =
|
|
Action.async { req =>
|
|
env.relay.api tourById TourModel.Id(id) map {
|
|
_ ?? { tour =>
|
|
apiC.GlobalConcurrencyLimitPerIP(HTTPRequest ipAddress req)(
|
|
env.relay.pgnStream(tour)
|
|
) { source =>
|
|
asAttachmentStream(s"${env.relay.pgnStream filename tour}.pgn")(
|
|
Ok chunked source as pgnContentType
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def apiIndex =
|
|
Action.async { implicit req =>
|
|
apiC.jsonStream {
|
|
env.relay.api
|
|
.officialTourStream(MaxPerSecond(20), getInt("nb", req) | 20)
|
|
.map(env.relay.jsonView.apply(_, withUrls = true))
|
|
}.fuccess
|
|
}
|
|
|
|
private def redirectToTour(tour: TourModel)(implicit ctx: Context): Fu[Result] =
|
|
env.relay.api.activeTourNextRound(tour) orElse env.relay.api.tourLastRound(tour) flatMap {
|
|
case None =>
|
|
ctx.me.?? { env.relay.api.canUpdate(_, tour) } flatMap {
|
|
_ ?? Redirect(routes.RelayRound.form(tour.id.value)).fuccess
|
|
}
|
|
case Some(round) => Redirect(round.withTour(tour).path).fuccess
|
|
}
|
|
|
|
private def WithTour(id: String)(
|
|
f: TourModel => Fu[Result]
|
|
)(implicit ctx: Context): Fu[Result] =
|
|
OptionFuResult(env.relay.api tourById TourModel.Id(id))(f)
|
|
|
|
private def WithTourCanUpdate(id: String)(
|
|
f: TourModel => Fu[Result]
|
|
)(implicit ctx: Context): Fu[Result] =
|
|
WithTour(id) { tour =>
|
|
ctx.me.?? { env.relay.api.canUpdate(_, tour) } flatMap {
|
|
_ ?? f(tour)
|
|
}
|
|
}
|
|
|
|
private val CreateLimitPerUser = new lila.memo.RateLimit[lila.user.User.ID](
|
|
credits = 10 * 10,
|
|
duration = 24.hour,
|
|
key = "broadcast.tournament.user"
|
|
)
|
|
|
|
private val CreateLimitPerIP = new lila.memo.RateLimit[IpAddress](
|
|
credits = 10 * 10,
|
|
duration = 24.hour,
|
|
key = "broadcast.tournament.ip"
|
|
)
|
|
|
|
private[controllers] def rateLimitCreation(
|
|
me: UserModel,
|
|
req: RequestHeader,
|
|
fail: => Result
|
|
)(
|
|
create: => Fu[Result]
|
|
): Fu[Result] = {
|
|
val cost =
|
|
if (isGranted(_.Relay, me)) 2
|
|
else if (me.hasTitle || me.isVerified) 5
|
|
else 10
|
|
CreateLimitPerUser(me.id, cost = cost) {
|
|
CreateLimitPerIP(HTTPRequest ipAddress req, cost = cost) {
|
|
create
|
|
}(fail.fuccess)
|
|
}(fail.fuccess)
|
|
}
|
|
}
|