typesafe ApiVersion
This commit is contained in:
parent
98cc5a8a32
commit
605f4a46b0
|
@ -15,10 +15,10 @@ object Api extends LilaController {
|
|||
val api = lila.api.Mobile.Api
|
||||
Ok(Json.obj(
|
||||
"api" -> Json.obj(
|
||||
"current" -> api.currentVersion,
|
||||
"current" -> api.currentVersion.value,
|
||||
"olds" -> api.oldVersions.map { old =>
|
||||
Json.obj(
|
||||
"version" -> old.version,
|
||||
"version" -> old.version.value,
|
||||
"deprecatedAt" -> old.deprecatedAt,
|
||||
"unsupportedAt" -> old.unsupportedAt)
|
||||
})
|
||||
|
|
|
@ -12,7 +12,7 @@ import scalaz.Monoid
|
|||
|
||||
import lila.api.{ PageData, Context, HeaderContext, BodyContext, TokenBucket }
|
||||
import lila.app._
|
||||
import lila.common.{ LilaCookie, HTTPRequest }
|
||||
import lila.common.{ LilaCookie, HTTPRequest, ApiVersion }
|
||||
import lila.notify.Notification.Notifies
|
||||
import lila.security.{ Permission, Granter, FingerprintedUser }
|
||||
import lila.user.{ UserContext, User => UserModel }
|
||||
|
@ -277,7 +277,7 @@ private[controllers] trait LilaController
|
|||
res,
|
||||
res withCookies LilaCookie.makeSessionId(req))
|
||||
|
||||
protected def negotiate(html: => Fu[Result], api: Int => Fu[Result])(implicit ctx: Context): Fu[Result] =
|
||||
protected def negotiate(html: => Fu[Result], api: ApiVersion => Fu[Result])(implicit ctx: Context): Fu[Result] =
|
||||
(lila.api.Mobile.Api.requestVersion(ctx.req) match {
|
||||
case Some(v) => api(v) map (_ as JSON)
|
||||
case _ => html
|
||||
|
|
|
@ -8,7 +8,7 @@ import play.twirl.api.Html
|
|||
|
||||
import lila.api.Context
|
||||
import lila.app._
|
||||
import lila.common.HTTPRequest
|
||||
import lila.common.{ HTTPRequest, ApiVersion }
|
||||
import lila.game.{ Pov, PlayerRef, GameRepo, Game => GameModel }
|
||||
import lila.hub.actorApi.map.Tell
|
||||
import lila.round.actorApi.round._
|
||||
|
@ -31,7 +31,9 @@ object Round extends LilaController with TheftPrevention {
|
|||
uid = uid,
|
||||
user = ctx.me,
|
||||
ip = ctx.ip,
|
||||
userTv = get("userTv"))
|
||||
userTv = get("userTv"),
|
||||
apiVersion = lila.api.Mobile.Api.currentVersion // yeah it should be in the URL
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +43,7 @@ object Round extends LilaController with TheftPrevention {
|
|||
if (isTheft(pov)) fuccess(Left(theftResponse))
|
||||
else get("sri") match {
|
||||
case Some(uid) => requestAiMove(pov) >> env.socketHandler.player(
|
||||
pov, uid, ~get("ran"), ctx.me, ctx.ip
|
||||
pov, uid, ~get("ran"), ctx.me, ctx.ip, ApiVersion(apiVersion)
|
||||
) map Right.apply
|
||||
case None => fuccess(Left(NotFound))
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
@jsTag("challenge.js")
|
||||
@embedJs {
|
||||
lichess.startChallenge(document.getElementById('challenge'), {
|
||||
socketUrl: '@routes.Challenge.websocket(c.id, apiVersion)',
|
||||
socketUrl: '@routes.Challenge.websocket(c.id, apiVersion.value)',
|
||||
xhrUrl: '@routes.Challenge.show(c.id)',
|
||||
owner: @owner,
|
||||
data: @Html(toJson(json))
|
||||
|
|
|
@ -16,7 +16,7 @@ explorer: {
|
|||
endpoint: "@explorerEndpoint",
|
||||
tablebaseEndpoint: "@tablebaseEndpoint"
|
||||
},
|
||||
socketUrl: "@routes.Study.websocket(s.id, apiVersion)",
|
||||
socketUrl: "@routes.Study.websocket(s.id, apiVersion.value)",
|
||||
socketVersion: @socketVersion
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,39 +4,41 @@ import org.joda.time.DateTime
|
|||
import play.api.http.HeaderNames
|
||||
import play.api.mvc.RequestHeader
|
||||
|
||||
import lila.common.ApiVersion
|
||||
|
||||
object Mobile {
|
||||
|
||||
object Api {
|
||||
|
||||
case class Old(
|
||||
version: Int,
|
||||
version: ApiVersion,
|
||||
// date when a newer version was released
|
||||
deprecatedAt: DateTime,
|
||||
// date when the server stops accepting requests
|
||||
unsupportedAt: DateTime)
|
||||
|
||||
val currentVersion = 2
|
||||
val currentVersion = ApiVersion(2)
|
||||
|
||||
val acceptedVersionNumbers = Set(1, 2)
|
||||
val acceptedVersions: Set[ApiVersion] = Set(1, 2) map ApiVersion.apply
|
||||
|
||||
val oldVersions: List[Old] = List(
|
||||
Old( // chat messages are html escaped
|
||||
version = 1,
|
||||
version = ApiVersion(1),
|
||||
deprecatedAt = new DateTime("2016-08-13"),
|
||||
unsupportedAt = new DateTime("2016-11-13"))
|
||||
)
|
||||
|
||||
private val PathPattern = """^.+/socket/v(\d+)$""".r
|
||||
|
||||
def requestVersion(req: RequestHeader): Option[Int] = {
|
||||
def requestVersion(req: RequestHeader): Option[ApiVersion] = {
|
||||
val accepts = ~req.headers.get(HeaderNames.ACCEPT)
|
||||
if (accepts contains "application/vnd.lichess.v2+json") Some(2)
|
||||
else if (accepts contains "application/vnd.lichess.v1+json") Some(1)
|
||||
if (accepts contains "application/vnd.lichess.v2+json") Some(ApiVersion(2))
|
||||
else if (accepts contains "application/vnd.lichess.v1+json") Some(ApiVersion(1))
|
||||
else req.path match {
|
||||
case PathPattern(version) => parseIntOption(version)
|
||||
case PathPattern(version) => parseIntOption(version) map ApiVersion.apply
|
||||
case _ => None
|
||||
}
|
||||
} filter acceptedVersionNumbers.contains
|
||||
} filter acceptedVersions.contains
|
||||
|
||||
def requested(req: RequestHeader) = requestVersion(req).isDefined
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ package lila.api
|
|||
import play.api.libs.json._
|
||||
|
||||
import lila.analyse.{ JsonView => analysisJson, Analysis, Info }
|
||||
import lila.common.LightUser
|
||||
import lila.common.PimpedJson._
|
||||
import lila.common.{ LightUser, ApiVersion }
|
||||
import lila.game.{ Pov, Game, GameRepo }
|
||||
import lila.pref.Pref
|
||||
import lila.round.{ JsonView, Forecast }
|
||||
|
@ -21,7 +21,7 @@ private[api] final class RoundApi(
|
|||
getSimul: Simul.ID => Fu[Option[Simul]],
|
||||
lightUser: String => Option[LightUser]) {
|
||||
|
||||
def player(pov: Pov, apiVersion: Int)(implicit ctx: Context): Fu[JsObject] =
|
||||
def player(pov: Pov, apiVersion: ApiVersion)(implicit ctx: Context): Fu[JsObject] =
|
||||
GameRepo.initialFen(pov.game) flatMap { initialFen =>
|
||||
jsonView.playerJson(pov, ctx.pref, apiVersion, ctx.me,
|
||||
withBlurs = ctx.me ?? Granter(_.ViewBlurs),
|
||||
|
@ -40,7 +40,7 @@ private[api] final class RoundApi(
|
|||
}
|
||||
}
|
||||
|
||||
def watcher(pov: Pov, apiVersion: Int, tv: Option[lila.round.OnTv],
|
||||
def watcher(pov: Pov, apiVersion: ApiVersion, tv: Option[lila.round.OnTv],
|
||||
initialFenO: Option[Option[String]] = None)(implicit ctx: Context): Fu[JsObject] =
|
||||
initialFenO.fold(GameRepo initialFen pov.game)(fuccess) flatMap { initialFen =>
|
||||
jsonView.watcherJson(pov, ctx.pref, apiVersion, ctx.me, tv,
|
||||
|
@ -60,7 +60,7 @@ private[api] final class RoundApi(
|
|||
}
|
||||
}
|
||||
|
||||
def review(pov: Pov, apiVersion: Int, tv: Option[lila.round.OnTv],
|
||||
def review(pov: Pov, apiVersion: ApiVersion, tv: Option[lila.round.OnTv],
|
||||
analysis: Option[Analysis] = None,
|
||||
initialFenO: Option[Option[String]] = None,
|
||||
withMoveTimes: Boolean = false,
|
||||
|
|
|
@ -5,6 +5,7 @@ import akka.pattern.{ ask, pipe }
|
|||
import play.api.libs.json.JsObject
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.common.ApiVersion
|
||||
import lila.analyse.Analysis
|
||||
import lila.game.Pov
|
||||
import lila.pref.Pref
|
||||
|
@ -20,11 +21,11 @@ private[api] final class RoundApiBalancer(
|
|||
|
||||
implicit val timeout = makeTimeout seconds 20
|
||||
|
||||
case class Player(pov: Pov, apiVersion: Int, ctx: Context)
|
||||
case class Watcher(pov: Pov, apiVersion: Int, tv: Option[lila.round.OnTv],
|
||||
case class Player(pov: Pov, apiVersion: ApiVersion, ctx: Context)
|
||||
case class Watcher(pov: Pov, apiVersion: ApiVersion, tv: Option[lila.round.OnTv],
|
||||
initialFenO: Option[Option[String]] = None,
|
||||
ctx: Context)
|
||||
case class Review(pov: Pov, apiVersion: Int, tv: Option[lila.round.OnTv],
|
||||
case class Review(pov: Pov, apiVersion: ApiVersion, tv: Option[lila.round.OnTv],
|
||||
analysis: Option[Analysis] = None,
|
||||
initialFenO: Option[Option[String]] = None,
|
||||
withMoveTimes: Boolean = false,
|
||||
|
@ -58,7 +59,7 @@ private[api] final class RoundApiBalancer(
|
|||
|
||||
import implementation._
|
||||
|
||||
def player(pov: Pov, apiVersion: Int)(implicit ctx: Context): Fu[JsObject] = {
|
||||
def player(pov: Pov, apiVersion: ApiVersion)(implicit ctx: Context): Fu[JsObject] = {
|
||||
router ? Player(pov, apiVersion, ctx) mapTo manifest[JsObject] addFailureEffect { e =>
|
||||
logger.error(pov.toString, e)
|
||||
}
|
||||
|
@ -67,12 +68,12 @@ private[api] final class RoundApiBalancer(
|
|||
.logIfSlow(500, logger) { _ => s"outer player $pov" }
|
||||
.result
|
||||
|
||||
def watcher(pov: Pov, apiVersion: Int, tv: Option[lila.round.OnTv],
|
||||
def watcher(pov: Pov, apiVersion: ApiVersion, tv: Option[lila.round.OnTv],
|
||||
initialFenO: Option[Option[String]] = None)(implicit ctx: Context): Fu[JsObject] = {
|
||||
router ? Watcher(pov, apiVersion, tv, initialFenO, ctx) mapTo manifest[JsObject]
|
||||
}.mon(_.round.api.watcher)
|
||||
|
||||
def review(pov: Pov, apiVersion: Int, tv: Option[lila.round.OnTv],
|
||||
def review(pov: Pov, apiVersion: ApiVersion, tv: Option[lila.round.OnTv],
|
||||
analysis: Option[Analysis] = None,
|
||||
initialFenO: Option[Option[String]] = None,
|
||||
withMoveTimes: Boolean = false,
|
||||
|
|
|
@ -13,6 +13,10 @@ trait PackageObject extends Steroids with WithFuture {
|
|||
def value: String
|
||||
override def toString = value
|
||||
}
|
||||
trait IntValue extends Any {
|
||||
def value: Int
|
||||
override def toString = value.toString
|
||||
}
|
||||
|
||||
def !![A](msg: String): Valid[A] = msg.failureNel[A]
|
||||
|
||||
|
|
3
modules/common/src/main/model.scala
Normal file
3
modules/common/src/main/model.scala
Normal file
|
@ -0,0 +1,3 @@
|
|||
package lila.common
|
||||
|
||||
case class ApiVersion(value: Int) extends AnyVal with IntValue
|
|
@ -7,6 +7,7 @@ import org.apache.commons.lang3.StringEscapeUtils.escapeHtml4
|
|||
import play.api.libs.json._
|
||||
|
||||
import lila.common.PimpedJson._
|
||||
import lila.common.ApiVersion
|
||||
import lila.game.JsonView._
|
||||
import lila.game.{ Pov, Game, PerfPicker, Source, GameRepo, CorrespondenceClock }
|
||||
import lila.pref.Pref
|
||||
|
@ -34,7 +35,7 @@ final class JsonView(
|
|||
def playerJson(
|
||||
pov: Pov,
|
||||
pref: Pref,
|
||||
apiVersion: Int,
|
||||
apiVersion: ApiVersion,
|
||||
playerUser: Option[User],
|
||||
initialFen: Option[String],
|
||||
withBlurs: Boolean): Fu[JsObject] =
|
||||
|
@ -119,7 +120,7 @@ final class JsonView(
|
|||
def watcherJson(
|
||||
pov: Pov,
|
||||
pref: Pref,
|
||||
apiVersion: Int,
|
||||
apiVersion: ApiVersion,
|
||||
user: Option[User],
|
||||
tv: Option[OnTv],
|
||||
withBlurs: Boolean,
|
||||
|
|
|
@ -141,9 +141,9 @@ private[round] final class Socket(
|
|||
blackIsGone = blackIsGone)
|
||||
} pipeTo sender
|
||||
|
||||
case Join(uid, user, color, playerId, ip, userTv) =>
|
||||
case Join(uid, user, color, playerId, ip, userTv, apiVersion) =>
|
||||
val (enumerator, channel) = Concurrent.broadcast[JsValue]
|
||||
val member = Member(channel, user, color, playerId, ip, userTv = userTv)
|
||||
val member = Member(channel, user, color, playerId, ip, userTv = userTv, apiVersion = apiVersion)
|
||||
addMember(uid, member)
|
||||
notifyCrowd
|
||||
playerDo(color, _.ping)
|
||||
|
|
|
@ -10,6 +10,7 @@ import chess.format.Uci
|
|||
import play.api.libs.json.{ JsObject, Json }
|
||||
|
||||
import actorApi._, round._
|
||||
import lila.common.ApiVersion
|
||||
import lila.common.PimpedJson._
|
||||
import lila.game.{ Game, Pov, PovRef, PlayerRef, GameRepo }
|
||||
import lila.hub.actorApi.map._
|
||||
|
@ -105,9 +106,10 @@ private[round] final class SocketHandler(
|
|||
uid: String,
|
||||
user: Option[User],
|
||||
ip: String,
|
||||
userTv: Option[String]): Fu[Option[JsSocketHandler]] =
|
||||
userTv: Option[String],
|
||||
apiVersion: ApiVersion): Fu[Option[JsSocketHandler]] =
|
||||
GameRepo.pov(gameId, colorName) flatMap {
|
||||
_ ?? { join(_, none, uid, "", user, ip, userTv = userTv) map some }
|
||||
_ ?? { join(_, none, uid, "", user, ip, userTv = userTv, apiVersion) map some }
|
||||
}
|
||||
|
||||
def player(
|
||||
|
@ -115,8 +117,9 @@ private[round] final class SocketHandler(
|
|||
uid: String,
|
||||
token: String,
|
||||
user: Option[User],
|
||||
ip: String): Fu[JsSocketHandler] =
|
||||
join(pov, Some(pov.playerId), uid, token, user, ip, userTv = none)
|
||||
ip: String,
|
||||
apiVersion: ApiVersion): Fu[JsSocketHandler] =
|
||||
join(pov, Some(pov.playerId), uid, token, user, ip, userTv = none, apiVersion)
|
||||
|
||||
private def join(
|
||||
pov: Pov,
|
||||
|
@ -125,14 +128,16 @@ private[round] final class SocketHandler(
|
|||
token: String,
|
||||
user: Option[User],
|
||||
ip: String,
|
||||
userTv: Option[String]): Fu[JsSocketHandler] = {
|
||||
userTv: Option[String],
|
||||
apiVersion: ApiVersion): Fu[JsSocketHandler] = {
|
||||
val join = Join(
|
||||
uid = uid,
|
||||
user = user,
|
||||
color = pov.color,
|
||||
playerId = playerId,
|
||||
ip = ip,
|
||||
userTv = userTv)
|
||||
userTv = userTv,
|
||||
apiVersion = apiVersion)
|
||||
socketHub ? Get(pov.gameId) mapTo manifest[ActorRef] flatMap { socket =>
|
||||
Handler(hub, socket, uid, join, user map (_.id)) {
|
||||
case Connected(enum, member) =>
|
||||
|
|
|
@ -7,6 +7,7 @@ import scala.concurrent.Promise
|
|||
import chess.Color
|
||||
import chess.format.Uci
|
||||
|
||||
import lila.common.ApiVersion
|
||||
import lila.game.{ Game, Event, PlayerRef }
|
||||
import lila.socket.SocketMember
|
||||
import lila.user.User
|
||||
|
@ -20,6 +21,7 @@ sealed trait Member extends SocketMember {
|
|||
val troll: Boolean
|
||||
val ip: String
|
||||
val userTv: Option[String]
|
||||
val apiVersion: ApiVersion
|
||||
|
||||
def owner = playerIdOption.isDefined
|
||||
def watcher = !owner
|
||||
|
@ -34,11 +36,12 @@ object Member {
|
|||
color: Color,
|
||||
playerIdOption: Option[String],
|
||||
ip: String,
|
||||
userTv: Option[String]): Member = {
|
||||
userTv: Option[String],
|
||||
apiVersion: ApiVersion): Member = {
|
||||
val userId = user map (_.id)
|
||||
val troll = user.??(_.troll)
|
||||
playerIdOption.fold[Member](Watcher(channel, userId, color, troll, ip, userTv)) { playerId =>
|
||||
Owner(channel, userId, playerId, color, troll, ip)
|
||||
playerIdOption.fold[Member](Watcher(channel, userId, color, troll, ip, userTv, apiVersion)) { playerId =>
|
||||
Owner(channel, userId, playerId, color, troll, ip, apiVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +52,8 @@ case class Owner(
|
|||
playerId: String,
|
||||
color: Color,
|
||||
troll: Boolean,
|
||||
ip: String) extends Member {
|
||||
ip: String,
|
||||
apiVersion: ApiVersion) extends Member {
|
||||
|
||||
val playerIdOption = playerId.some
|
||||
val userTv = none
|
||||
|
@ -61,7 +65,8 @@ case class Watcher(
|
|||
color: Color,
|
||||
troll: Boolean,
|
||||
ip: String,
|
||||
userTv: Option[String]) extends Member {
|
||||
userTv: Option[String],
|
||||
apiVersion: ApiVersion) extends Member {
|
||||
|
||||
val playerIdOption = none
|
||||
}
|
||||
|
@ -72,7 +77,8 @@ case class Join(
|
|||
color: Color,
|
||||
playerId: Option[String],
|
||||
ip: String,
|
||||
userTv: Option[String])
|
||||
userTv: Option[String],
|
||||
apiVersion: ApiVersion)
|
||||
case class Connected(enumerator: JsEnumerator, member: Member)
|
||||
case class Bye(color: Color)
|
||||
case class IsGone(color: Color)
|
||||
|
|
|
@ -7,6 +7,7 @@ import play.api.data.Forms._
|
|||
import play.api.mvc.RequestHeader
|
||||
import reactivemongo.bson._
|
||||
|
||||
import lila.common.ApiVersion
|
||||
import lila.db.BSON.BSONJodaDateTimeHandler
|
||||
import lila.db.dsl._
|
||||
import lila.user.{ User, UserRepo }
|
||||
|
@ -46,7 +47,7 @@ final class Api(
|
|||
private def authenticateCandidate(candidate: Option[User.LoginCandidate])(username: String, password: String): Option[User] =
|
||||
candidate ?? { _(password) }
|
||||
|
||||
def saveAuthentication(userId: String, apiVersion: Option[Int])(implicit req: RequestHeader): Fu[String] =
|
||||
def saveAuthentication(userId: String, apiVersion: Option[ApiVersion])(implicit req: RequestHeader): Fu[String] =
|
||||
UserRepo mustConfirmEmail userId flatMap {
|
||||
case true => fufail(Api MustConfirmEmail userId)
|
||||
case false =>
|
||||
|
|
|
@ -6,10 +6,10 @@ import org.joda.time.DateTime
|
|||
import play.api.mvc.RequestHeader
|
||||
import reactivemongo.bson.Macros
|
||||
|
||||
import lila.db.dsl._
|
||||
import lila.common.{ HTTPRequest, ApiVersion }
|
||||
import lila.db.BSON.BSONJodaDateTimeHandler
|
||||
import lila.db.dsl._
|
||||
import lila.user.{ User, UserRepo }
|
||||
import lila.common.HTTPRequest
|
||||
|
||||
object Store {
|
||||
|
||||
|
@ -20,7 +20,7 @@ object Store {
|
|||
sessionId: String,
|
||||
userId: String,
|
||||
req: RequestHeader,
|
||||
apiVersion: Option[Int]): Funit =
|
||||
apiVersion: Option[ApiVersion]): Funit =
|
||||
coll.insert($doc(
|
||||
"_id" -> sessionId,
|
||||
"user" -> userId,
|
||||
|
@ -28,13 +28,16 @@ object Store {
|
|||
"ua" -> HTTPRequest.userAgent(req).|("?"),
|
||||
"date" -> DateTime.now,
|
||||
"up" -> true,
|
||||
"api" -> apiVersion
|
||||
"api" -> apiVersion.map(_.value)
|
||||
)).void
|
||||
|
||||
private val userIdProjection = $doc("user" -> true, "_id" -> false)
|
||||
private val userIdFingerprintProjection = $doc("user" -> true, "fp" -> true, "_id" -> false)
|
||||
|
||||
def userId(sessionId: String): Fu[Option[String]] =
|
||||
coll.find(
|
||||
$doc("_id" -> sessionId, "up" -> true),
|
||||
$doc("user" -> true, "_id" -> false)
|
||||
userIdProjection
|
||||
).uno[Bdoc] map { _ flatMap (_.getAs[String]("user")) }
|
||||
|
||||
case class UserIdAndFingerprint(user: String, fp: Option[String])
|
||||
|
@ -43,7 +46,7 @@ object Store {
|
|||
def userIdAndFingerprint(sessionId: String): Fu[Option[UserIdAndFingerprint]] =
|
||||
coll.find(
|
||||
$doc("_id" -> sessionId, "up" -> true),
|
||||
$doc("user" -> true, "fp" -> true, "_id" -> false)
|
||||
userIdFingerprintProjection
|
||||
).uno[UserIdAndFingerprint]
|
||||
|
||||
def delete(sessionId: String): Funit =
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.joda.time.DateTime
|
|||
import reactivemongo.api._
|
||||
import reactivemongo.bson._
|
||||
|
||||
import lila.common.ApiVersion
|
||||
import lila.db.BSON.BSONJodaDateTimeHandler
|
||||
import lila.db.dsl._
|
||||
import lila.rating.{ Glicko, Perf, PerfType }
|
||||
|
@ -219,7 +220,7 @@ object UserRepo {
|
|||
def getPasswordHash(id: ID): Fu[Option[String]] =
|
||||
coll.primitiveOne[String]($id(id), "password")
|
||||
|
||||
def create(username: String, password: String, email: Option[String], blind: Boolean, mobileApiVersion: Option[Int]): Fu[Option[User]] =
|
||||
def create(username: String, password: String, email: Option[String], blind: Boolean, mobileApiVersion: Option[ApiVersion]): Fu[Option[User]] =
|
||||
!nameExists(username) flatMap {
|
||||
_ ?? {
|
||||
val doc = newUser(username, password, email, blind, mobileApiVersion) ++
|
||||
|
@ -347,7 +348,7 @@ object UserRepo {
|
|||
|
||||
def setEmailConfirmed(id: String): Funit = coll.update($id(id), $unset(F.mustConfirmEmail)).void
|
||||
|
||||
private def newUser(username: String, password: String, email: Option[String], blind: Boolean, mobileApiVersion: Option[Int]) = {
|
||||
private def newUser(username: String, password: String, email: Option[String], blind: Boolean, mobileApiVersion: Option[ApiVersion]) = {
|
||||
|
||||
val salt = ornicar.scalalib.Random nextStringUppercase 32
|
||||
implicit def countHandler = Count.countBSONHandler
|
||||
|
@ -365,7 +366,7 @@ object UserRepo {
|
|||
F.count -> Count.default,
|
||||
F.enabled -> true,
|
||||
F.createdAt -> DateTime.now,
|
||||
F.createdWithApiVersion -> mobileApiVersion,
|
||||
F.createdWithApiVersion -> mobileApiVersion.map(_.value),
|
||||
F.seenAt -> DateTime.now) ++ {
|
||||
if (blind) $doc("blind" -> true) else $empty
|
||||
}
|
||||
|
|
|
@ -2007,6 +2007,10 @@ body.base .crosstable .loss {
|
|||
.crosstable .user {
|
||||
padding-left: 7px;
|
||||
}
|
||||
.crosstable .unavailable {
|
||||
margin-top: 40px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.bookmark {
|
||||
float: right;
|
||||
outline: none;
|
||||
|
|
Loading…
Reference in a new issue